fmod 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.travis.yml +5 -0
- data/.yardopts +2 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +96 -0
- data/Rakefile +1 -0
- data/bin/console +28 -0
- data/bin/setup +8 -0
- data/ext/fmod.dll +0 -0
- data/ext/fmod64.dll +0 -0
- data/ext/libfmod.dylib +0 -0
- data/ext/llbfmod.zip +0 -0
- data/extras/FMOD Studio Programmers API for Windows.chm +0 -0
- data/fmod.gemspec +58 -0
- data/lib/fmod.rb +564 -0
- data/lib/fmod/channel.rb +151 -0
- data/lib/fmod/channel_control.rb +821 -0
- data/lib/fmod/channel_group.rb +61 -0
- data/lib/fmod/core.rb +35 -0
- data/lib/fmod/core/bool_description.rb +18 -0
- data/lib/fmod/core/channel_mask.rb +24 -0
- data/lib/fmod/core/data_description.rb +14 -0
- data/lib/fmod/core/driver.rb +59 -0
- data/lib/fmod/core/dsp_description.rb +7 -0
- data/lib/fmod/core/dsp_index.rb +9 -0
- data/lib/fmod/core/dsp_type.rb +43 -0
- data/lib/fmod/core/extensions.rb +28 -0
- data/lib/fmod/core/file_system.rb +86 -0
- data/lib/fmod/core/filter_type.rb +19 -0
- data/lib/fmod/core/float_description.rb +16 -0
- data/lib/fmod/core/guid.rb +50 -0
- data/lib/fmod/core/init_flags.rb +19 -0
- data/lib/fmod/core/integer_description.rb +26 -0
- data/lib/fmod/core/mode.rb +36 -0
- data/lib/fmod/core/output_type.rb +30 -0
- data/lib/fmod/core/parameter_info.rb +41 -0
- data/lib/fmod/core/parameter_type.rb +10 -0
- data/lib/fmod/core/result.rb +88 -0
- data/lib/fmod/core/reverb.rb +217 -0
- data/lib/fmod/core/sound_ex_info.rb +7 -0
- data/lib/fmod/core/sound_format.rb +30 -0
- data/lib/fmod/core/sound_group_behavior.rb +9 -0
- data/lib/fmod/core/sound_type.rb +80 -0
- data/lib/fmod/core/speaker_index.rb +18 -0
- data/lib/fmod/core/speaker_mode.rb +16 -0
- data/lib/fmod/core/spectrum_data.rb +12 -0
- data/lib/fmod/core/structure.rb +23 -0
- data/lib/fmod/core/structures.rb +41 -0
- data/lib/fmod/core/tag.rb +51 -0
- data/lib/fmod/core/tag_data_type.rb +14 -0
- data/lib/fmod/core/time_unit.rb +40 -0
- data/lib/fmod/core/vector.rb +42 -0
- data/lib/fmod/core/window_type.rb +12 -0
- data/lib/fmod/dsp.rb +510 -0
- data/lib/fmod/dsp_connection.rb +113 -0
- data/lib/fmod/effects.rb +38 -0
- data/lib/fmod/effects/channel_mix.rb +101 -0
- data/lib/fmod/effects/chorus.rb +30 -0
- data/lib/fmod/effects/compressor.rb +52 -0
- data/lib/fmod/effects/convolution_reverb.rb +31 -0
- data/lib/fmod/effects/delay.rb +44 -0
- data/lib/fmod/effects/distortion.rb +16 -0
- data/lib/fmod/effects/dsps.rb +10 -0
- data/lib/fmod/effects/echo.rb +37 -0
- data/lib/fmod/effects/envelope_follower.rb +31 -0
- data/lib/fmod/effects/fader.rb +16 -0
- data/lib/fmod/effects/fft.rb +38 -0
- data/lib/fmod/effects/flange.rb +37 -0
- data/lib/fmod/effects/high_pass.rb +24 -0
- data/lib/fmod/effects/high_pass_simple.rb +25 -0
- data/lib/fmod/effects/it_echo.rb +56 -0
- data/lib/fmod/effects/it_lowpass.rb +36 -0
- data/lib/fmod/effects/ladspa_plugin.rb +14 -0
- data/lib/fmod/effects/limiter.rb +32 -0
- data/lib/fmod/effects/loudness_meter.rb +19 -0
- data/lib/fmod/effects/low_pass.rb +25 -0
- data/lib/fmod/effects/low_pass_simple.rb +26 -0
- data/lib/fmod/effects/mixer.rb +11 -0
- data/lib/fmod/effects/multiband_eq.rb +153 -0
- data/lib/fmod/effects/normalize.rb +47 -0
- data/lib/fmod/effects/object_pan.rb +62 -0
- data/lib/fmod/effects/oscillator.rb +52 -0
- data/lib/fmod/effects/pan.rb +166 -0
- data/lib/fmod/effects/param_eq.rb +36 -0
- data/lib/fmod/effects/pitch_shift.rb +47 -0
- data/lib/fmod/effects/return.rb +18 -0
- data/lib/fmod/effects/send.rb +21 -0
- data/lib/fmod/effects/sfx_reverb.rb +87 -0
- data/lib/fmod/effects/three_eq.rb +41 -0
- data/lib/fmod/effects/transceiver.rb +57 -0
- data/lib/fmod/effects/tremolo.rb +67 -0
- data/lib/fmod/effects/vst_plugin.rb +12 -0
- data/lib/fmod/effects/winamp_plugin.rb +12 -0
- data/lib/fmod/error.rb +108 -0
- data/lib/fmod/geometry.rb +380 -0
- data/lib/fmod/handle.rb +129 -0
- data/lib/fmod/reverb3D.rb +98 -0
- data/lib/fmod/sound.rb +810 -0
- data/lib/fmod/sound_group.rb +54 -0
- data/lib/fmod/system.rb +1242 -0
- data/lib/fmod/version.rb +3 -0
- metadata +220 -0
data/lib/fmod/channel.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
|
2
|
+
module FMOD
|
3
|
+
class Channel < ChannelControl
|
4
|
+
|
5
|
+
##
|
6
|
+
# @!attribute frequency
|
7
|
+
# This value can also be negative to play the sound backwards (negative
|
8
|
+
# frequencies allowed with non-stream sounds only).
|
9
|
+
#
|
10
|
+
# When a sound is played, it plays at the default frequency of the sound
|
11
|
+
# which can be set by {Sound.default_frequency}.
|
12
|
+
#
|
13
|
+
# For most file formats, the default frequency is determined by the audio
|
14
|
+
# format.
|
15
|
+
#
|
16
|
+
# @return [Float] the channel frequency or playback rate, in Hz.
|
17
|
+
float_reader(:frequency, :Channel_GetFrequency)
|
18
|
+
float_writer(:frequency=, :Channel_SetFrequency)
|
19
|
+
|
20
|
+
##
|
21
|
+
# @!attribute [r] index
|
22
|
+
# @return [Integer] the internal channel index for a channel.
|
23
|
+
integer_reader(:index, :Channel_GetIndex)
|
24
|
+
|
25
|
+
##
|
26
|
+
# @!attribute priority
|
27
|
+
# The channel priority.
|
28
|
+
#
|
29
|
+
# When more channels than available are played the virtual channel system
|
30
|
+
# will choose existing channels to steal. Lower priority sounds will always
|
31
|
+
# be stolen before higher priority sounds. For channels of equal priority,
|
32
|
+
# that with the quietest {ChannelControl.audibility} value will be stolen.
|
33
|
+
# * *Minimum:* 0
|
34
|
+
# * *Maximum:* 256
|
35
|
+
# * *Default:* 128
|
36
|
+
# @return [Integer] the current priority.
|
37
|
+
integer_reader(:priority, :Channel_GetPriority)
|
38
|
+
integer_writer(:priority=, :Channel_SetPriority, 0, 256)
|
39
|
+
|
40
|
+
##
|
41
|
+
# @!attribute loop_count
|
42
|
+
# Sets a sound, by default, to loop a specified number of times before
|
43
|
+
# stopping if its mode is set to {Mode::LOOP_NORMAL} or {Mode::LOOP_BIDI}.
|
44
|
+
# @return [Integer] the number of times to loop a sound before stopping.
|
45
|
+
integer_reader(:loop_count, :Channel_GetLoopCount)
|
46
|
+
integer_writer(:loop_count=, :Channel_SetLoopCount, -1)
|
47
|
+
|
48
|
+
##
|
49
|
+
# @!attribute [r] current_sound
|
50
|
+
# @return [Sound, nil] the currently playing sound for this channel, or
|
51
|
+
# +nil+ if no sound is playing.
|
52
|
+
def current_sound
|
53
|
+
FMOD.invoke(:Channel_GetCurrentSound, self, sound = int_ptr)
|
54
|
+
sound.unpack1('J').zero? ? nil : Sound.new(sound)
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# @!method virtual?
|
59
|
+
# Retrieves whether the channel is virtual (emulated) or not due to the
|
60
|
+
# virtual channel management system
|
61
|
+
# @return [Boolean] the current virtual state.
|
62
|
+
# * *true:* Inaudible and currently being emulated at no CPU cost
|
63
|
+
# * *false:* Real voice that should be audible.
|
64
|
+
bool_reader(:virtual?, :Channel_IsVirtual)
|
65
|
+
|
66
|
+
##
|
67
|
+
# Returns the current playback position.
|
68
|
+
# @param unit [Integer] Time unit to retrieve into the position in.
|
69
|
+
# @see TimeUnit
|
70
|
+
# @return [Integer] the current playback position.
|
71
|
+
def position(unit = TimeUnit::MS)
|
72
|
+
buffer = "\0" * SIZEOF_INT
|
73
|
+
FMOD.invoke(:Channel_SetPosition, self, buffer, unit)
|
74
|
+
buffer.unpack1('L')
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Sets the playback position for the currently playing sound to the
|
79
|
+
# specified offset.
|
80
|
+
# @param position [Integer] Position of the channel to set in specified
|
81
|
+
# units.
|
82
|
+
# @param unit [Integer] Time unit to set the channel position by.
|
83
|
+
# @see TimeUnit
|
84
|
+
# @return [self]
|
85
|
+
def seek(position, unit = TimeUnit::MS)
|
86
|
+
position = 0 if position < 0
|
87
|
+
FMOD.invoke(:Channel_SetPosition, self, position, unit)
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# @!attribute group
|
93
|
+
# @return [ChannelGroup] the currently assigned channel group for this
|
94
|
+
# {Channel}.
|
95
|
+
|
96
|
+
def group
|
97
|
+
FMOD.invoke(:Channel_GetChannelGroup, self, group = int_ptr)
|
98
|
+
ChannelGroup.new(group)
|
99
|
+
end
|
100
|
+
|
101
|
+
def group=(channel_group)
|
102
|
+
FMOD.type?(channel_group, ChannelGroup)
|
103
|
+
FMOD.invoke(:Channel_SetChannelGroup, self, channel_group)
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Retrieves the loop points for a sound.
|
108
|
+
# @param start_unit [Integer] The time format used for the returned loop
|
109
|
+
# start point.
|
110
|
+
# @see TimeUnit
|
111
|
+
# @param end_unit [Integer] The time format used for the returned loop end
|
112
|
+
# point.
|
113
|
+
# @see TimeUnit
|
114
|
+
# @return [Array(Integer, Integer)] the loop points in an array where the
|
115
|
+
# first element is the start loop point, and second element is the end
|
116
|
+
# loop point in the requested time units.
|
117
|
+
def loop_points(start_unit = TimeUnit::MS, end_unit = TimeUnit::MS)
|
118
|
+
loop_start, loop_end = "\0" * SIZEOF_INT, "\0" * SIZEOF_INT
|
119
|
+
FMOD.invoke(:Channel_GetLoopPoints, self, loop_start,
|
120
|
+
start_unit, loop_end, end_unit)
|
121
|
+
[loop_start.unpack1('L'), loop_end.unpack1('L')]
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# Sets the loop points within a sound
|
126
|
+
#
|
127
|
+
# If a sound was 44100 samples long and you wanted to loop the whole sound,
|
128
|
+
# _loop_start_ would be 0, and _loop_end_ would be 44099, not 44100. You
|
129
|
+
# wouldn't use milliseconds in this case because they are not sample
|
130
|
+
# accurate.
|
131
|
+
#
|
132
|
+
# If loop end is smaller or equal to loop start, it will result in an error.
|
133
|
+
#
|
134
|
+
# If loop start or loop end is larger than the length of the sound, it will
|
135
|
+
# result in an error
|
136
|
+
#
|
137
|
+
# @param loop_start [Integer] The loop start point. This point in time is
|
138
|
+
# played, so it is inclusive.
|
139
|
+
# @param loop_end [Integer] The loop end point. This point in time is
|
140
|
+
# played, so it is inclusive
|
141
|
+
# @param start_unit [Integer] The time format used for the loop start point.
|
142
|
+
# @see TimeUnit
|
143
|
+
# @param end_unit [Integer] The time format used for the loop end point.
|
144
|
+
# @see TimeUnit
|
145
|
+
def set_loop(loop_start, loop_end, start_unit = TimeUnit::MS, end_unit = TimeUnit::MS)
|
146
|
+
FMOD.invoke(:Channel_SetLoopPoints, self, loop_start,
|
147
|
+
start_unit, loop_end, end_unit)
|
148
|
+
self
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,821 @@
|
|
1
|
+
|
2
|
+
module FMOD
|
3
|
+
class ChannelControl < Handle
|
4
|
+
|
5
|
+
ChannelDelay = Struct.new(:start, :end, :stop)
|
6
|
+
|
7
|
+
DistanceFilter = Struct.new(:custom, :level, :frequency)
|
8
|
+
|
9
|
+
include Fiddle
|
10
|
+
include FMOD::Core
|
11
|
+
|
12
|
+
##
|
13
|
+
# @!attribute volume
|
14
|
+
# Gets or sets the linear volume level.
|
15
|
+
#
|
16
|
+
# Volume level can be below 0.0 to invert a signal and above 1.0 to
|
17
|
+
# amplify the signal. Note that increasing the signal level too far may
|
18
|
+
# cause audible distortion.
|
19
|
+
#
|
20
|
+
# @return [Float]
|
21
|
+
float_reader(:volume, :ChannelGroup_GetVolume)
|
22
|
+
float_writer(:volume=, :ChannelGroup_SetVolume)
|
23
|
+
|
24
|
+
##
|
25
|
+
# @!attribute volume_ramp
|
26
|
+
# Gets or sets flag indicating whether the channel automatically ramps
|
27
|
+
# when setting volumes.
|
28
|
+
#
|
29
|
+
# When changing volumes on a non-paused channel, FMOD normally adds a
|
30
|
+
# small ramp to avoid a pop sound. This function allows that setting to be
|
31
|
+
# overridden and volume changes to be applied immediately.
|
32
|
+
#
|
33
|
+
# @return [Boolean]
|
34
|
+
bool_reader(:volume_ramp, :ChannelGroup_GetVolumeRamp)
|
35
|
+
bool_writer(:volume_ramp=, :ChannelGroup_SetVolumeRamp)
|
36
|
+
|
37
|
+
##
|
38
|
+
# @!attribute pitch
|
39
|
+
# Sets the pitch value.
|
40
|
+
#
|
41
|
+
# This function scales existing frequency values by the pitch.
|
42
|
+
# * *0.5:* One octave lower
|
43
|
+
# * *2.0:* One octave higher
|
44
|
+
# * *1.0:* Normal pitch
|
45
|
+
# @return [Float]
|
46
|
+
float_reader(:pitch, :ChannelGroup_GetPitch)
|
47
|
+
float_writer(:pitch=, :ChannelGroup_SetPitch)
|
48
|
+
|
49
|
+
##
|
50
|
+
# @!attribute mode
|
51
|
+
# Changes some attributes for a {ChannelControl} based on the mode passed in.
|
52
|
+
#
|
53
|
+
# Supported flags:
|
54
|
+
# * {Mode::LOOP_OFF}
|
55
|
+
# * {Mode::LOOP_NORMAL}
|
56
|
+
# * {Mode::LOOP_BIDI}
|
57
|
+
# * {Mode::TWO_D}
|
58
|
+
# * {Mode::THREE_D}
|
59
|
+
# * {Mode::HEAD_RELATIVE_3D}
|
60
|
+
# * {Mode::WORLD_RELATIVE_3D}
|
61
|
+
# * {Mode::INVERSE_ROLLOFF_3D}
|
62
|
+
# * {Mode::LINEAR_ROLLOFF_3D}
|
63
|
+
# * {Mode::LINEAR_SQUARE_ROLLOFF_3D}
|
64
|
+
# * {Mode::CUSTOM_ROLLOFF_3D}
|
65
|
+
# * {Mode::IGNORE_GEOMETRY_3D}
|
66
|
+
# * {Mode::VIRTUAL_PLAY_FROM_START}
|
67
|
+
#
|
68
|
+
# When changing the loop mode, sounds created with {Mode::CREATE_STREAM}
|
69
|
+
# may have already been pre-buffered and executed their loop logic ahead
|
70
|
+
# of time before this call was even made. This is dependant on the size of
|
71
|
+
# the sound versus the size of the stream decode buffer. If this happens,
|
72
|
+
# you may need to re-flush the stream buffer by calling {Channel.seek}.
|
73
|
+
# Note this will usually only happen if you have sounds or loop points
|
74
|
+
# that are smaller than the stream decode buffer size.
|
75
|
+
#
|
76
|
+
# @return [Integer] Mode bits.
|
77
|
+
integer_reader(:mode, :ChannelGroup_GetMode)
|
78
|
+
integer_writer(:mode=, :ChannelGroup_SetMode)
|
79
|
+
|
80
|
+
##
|
81
|
+
# @!method playing?
|
82
|
+
# @return [Boolean] the playing state.
|
83
|
+
bool_reader(:playing?, :ChannelGroup_IsPlaying)
|
84
|
+
|
85
|
+
##
|
86
|
+
# @!method paused?
|
87
|
+
# @return [Boolean] the paused state.
|
88
|
+
bool_reader(:paused?, :ChannelGroup_GetPaused)
|
89
|
+
|
90
|
+
##
|
91
|
+
# @!method muted?
|
92
|
+
# @return [Boolean] the mute state.
|
93
|
+
bool_reader(:muted?, :ChannelGroup_GetMute)
|
94
|
+
|
95
|
+
##
|
96
|
+
# @!attribute [r] audibility
|
97
|
+
# The combined volume after 3D spatialization and geometry occlusion
|
98
|
+
# calculations including any volumes set via the API.
|
99
|
+
#
|
100
|
+
# This does not represent the waveform, just the calculated result of all
|
101
|
+
# volume modifiers. This value is used by the virtual channel system to
|
102
|
+
# order its channels between real and virtual.
|
103
|
+
#
|
104
|
+
# @return [Float] the combined volume after 3D spatialization and geometry
|
105
|
+
# occlusion.
|
106
|
+
float_reader(:audibility, :ChannelGroup_GetAudibility)
|
107
|
+
|
108
|
+
float_reader(:low_pass_gain, :ChannelGroup_GetLowPassGain)
|
109
|
+
float_writer(:low_pass_gain=, :ChannelGroup_SetLowPassGain, 0.0, 1.0)
|
110
|
+
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
|
120
|
+
|
121
|
+
|
122
|
+
|
123
|
+
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
|
130
|
+
|
131
|
+
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
|
139
|
+
|
140
|
+
|
141
|
+
|
142
|
+
|
143
|
+
float_reader(:level3D, :ChannelGroup_Get3DLevel)
|
144
|
+
float_writer(:level3D=, :ChannelGroup_Set3DLevel, 0.0, 1.0)
|
145
|
+
|
146
|
+
float_reader(:spread3D, :ChannelGroup_Get3DSpread)
|
147
|
+
float_writer(:spread3D, :ChannelGroup_Set3DSpread, 0.0, 360.0)
|
148
|
+
|
149
|
+
float_reader(:doppler3D, :ChannelGroup_Get3DDopplerLevel)
|
150
|
+
float_writer(:doppler3D=, :ChannelGroup_Set3DDopplerLevel, 0.0, 5.0)
|
151
|
+
|
152
|
+
def direct_occlusion
|
153
|
+
direct = "\0" * SIZEOF_FLOAT
|
154
|
+
FMOD.invoke(:ChannelGroup_Get3DOcclusion, self, direct, nil)
|
155
|
+
direct.unpack1('f')
|
156
|
+
end
|
157
|
+
|
158
|
+
def direct_occlusion=(direct)
|
159
|
+
direct = direct.clamp(0.0, 1.0)
|
160
|
+
reverb = reverb_occlusion
|
161
|
+
FMOD.invoke(:ChannelGroup_Set3DOcclusion, self, direct, reverb)
|
162
|
+
direct
|
163
|
+
end
|
164
|
+
|
165
|
+
def reverb_occlusion
|
166
|
+
reverb = "\0" * SIZEOF_FLOAT
|
167
|
+
FMOD.invoke(:ChannelGroup_Get3DOcclusion, self, nil, reverb)
|
168
|
+
reverb.unpack1('f')
|
169
|
+
end
|
170
|
+
|
171
|
+
def reverb_occlusion=(reverb)
|
172
|
+
direct = direct_occlusion
|
173
|
+
reverb = reverb.clamp(0.0, 1.0)
|
174
|
+
FMOD.invoke(:ChannelGroup_Set3DOcclusion, self, direct, reverb)
|
175
|
+
reverb
|
176
|
+
end
|
177
|
+
|
178
|
+
##
|
179
|
+
# Add a volume point to fade from or towards, using a clock offset and 0.0
|
180
|
+
# to 1.0 volume level.
|
181
|
+
# @overload add_fade(fade_point)
|
182
|
+
# @param fade_point [FadePoint] Fade point structure defining the values.
|
183
|
+
# @overload add_fade(clock, volume)
|
184
|
+
# @param clock [Integer] DSP clock of the parent channel group to set the
|
185
|
+
# fade point volume.
|
186
|
+
# @param volume [Float] Volume level where 0.0 is silent and 1.0 is normal
|
187
|
+
# volume. Amplification is supported.
|
188
|
+
# @return [self]
|
189
|
+
def add_fade(*args)
|
190
|
+
args = args[0].values if args.size == 1 && args[0].is_a?(FadePoint)
|
191
|
+
FMOD.invoke(:ChannelGroup_AddFadePoint, self, *args)
|
192
|
+
self
|
193
|
+
end
|
194
|
+
|
195
|
+
##
|
196
|
+
# Retrieves the number of fade points set within the {ChannelControl}.
|
197
|
+
# @return [Integer] The number of fade points.
|
198
|
+
def fade_point_count
|
199
|
+
count = "\0" * SIZEOF_INT
|
200
|
+
FMOD.invoke(:ChannelGroup_GetFadePoints, self, count, nil, nil)
|
201
|
+
count.unpack1('l')
|
202
|
+
end
|
203
|
+
|
204
|
+
##
|
205
|
+
# Retrieve information about fade points stored within a {ChannelControl}.
|
206
|
+
# @return [Array<FadePoint>] An array of {FadePoint} objects, or an empty
|
207
|
+
# array if no fade points are present.
|
208
|
+
def fade_points
|
209
|
+
count = fade_point_count
|
210
|
+
return [] if count.zero?
|
211
|
+
clocks = "\0" * (count * SIZEOF_LONG_LONG)
|
212
|
+
volumes = "\0" * (count * SIZEOF_FLOAT)
|
213
|
+
FMOD.invoke(:ChannelGroup_GetFadePoints, self, int_ptr, clocks, volumes)
|
214
|
+
args = clocks.unpack('Q*').zip(volumes.unpack('f*'))
|
215
|
+
args.map { |values| FadePoint.new(*values) }
|
216
|
+
end
|
217
|
+
|
218
|
+
##
|
219
|
+
# Remove volume fade points on the time-line. This function will remove
|
220
|
+
# multiple fade points with a single call if the points lay between the 2
|
221
|
+
# specified clock values (inclusive).
|
222
|
+
# @param clock_start [Integer] DSP clock of the parent channel group to
|
223
|
+
# start removing fade points from.
|
224
|
+
# @param clock_end [Integer] DSP clock of the parent channel group to start
|
225
|
+
# removing fade points to.
|
226
|
+
# @return [self]
|
227
|
+
def remove_fade_points(clock_start, clock_end)
|
228
|
+
FMOD.invoke(:ChannelGroup_RemoveFadePoints, self, clock_start, clock_end)
|
229
|
+
self
|
230
|
+
end
|
231
|
+
|
232
|
+
def position3D
|
233
|
+
position = Vector.zero
|
234
|
+
FMOD.invoke(:ChannelGroup_Get3DAttributes, self, position, nil, nil)
|
235
|
+
position
|
236
|
+
end
|
237
|
+
|
238
|
+
def position3D=(vector)
|
239
|
+
FMOD.type?(vector, Vector)
|
240
|
+
FMOD.invoke(:ChannelGroup_Set3DAttributes, self, vector, nil, nil)
|
241
|
+
vector
|
242
|
+
end
|
243
|
+
|
244
|
+
def velocity3D
|
245
|
+
velocity = Vector.zero
|
246
|
+
FMOD.invoke(:ChannelGroup_Get3DAttributes, self, nil, velocity, nil)
|
247
|
+
velocity
|
248
|
+
end
|
249
|
+
|
250
|
+
def velocity3D=(vector)
|
251
|
+
FMOD.type?(vector, Vector)
|
252
|
+
FMOD.invoke(:ChannelGroup_Set3DAttributes, self, nil, vector, nil)
|
253
|
+
vector
|
254
|
+
end
|
255
|
+
|
256
|
+
def distance_filter
|
257
|
+
args = ["\0" * SIZEOF_INT, "\0" * SIZEOF_FLOAT, "\0" * SIZEOF_FLOAT]
|
258
|
+
FMOD.invoke(:ChannelGroup_Get3DDistanceFilter, self, *args)
|
259
|
+
args = args.join.unpack('lff')
|
260
|
+
args[0] = args[0] != 0
|
261
|
+
DistanceFilter.new(*args)
|
262
|
+
end
|
263
|
+
|
264
|
+
def distance_filter=(filter)
|
265
|
+
FMOD.type?(filter, DistanceFilter)
|
266
|
+
args = filter.values
|
267
|
+
args[0] = args[0].to_i
|
268
|
+
FMOD.invoke(:ChannelGroup_Set3DDistanceFilter, self, *args)
|
269
|
+
end
|
270
|
+
|
271
|
+
##
|
272
|
+
# @!attribute custom_rolloff
|
273
|
+
# A custom rolloff curve to define how audio will attenuate over distance.
|
274
|
+
#
|
275
|
+
# Must be used in conjunction with {Mode::CUSTOM_ROLLOFF_3D} flag to be
|
276
|
+
# activated.
|
277
|
+
#
|
278
|
+
# <b>Points must be sorted by distance! Passing an unsorted list to FMOD
|
279
|
+
# will result in an error.</b>
|
280
|
+
# @return [Array<Vector>] the rolloff curve.
|
281
|
+
|
282
|
+
def custom_rolloff
|
283
|
+
count = "\0" * SIZEOF_INT
|
284
|
+
FMOD.invoke(:ChannelGroup_Get3DCustomRolloff, self, nil, count)
|
285
|
+
count = count.unpack1('l')
|
286
|
+
return [] if count.zero?
|
287
|
+
size = SIZEOF_FLOAT * 3
|
288
|
+
FMOD.invoke(:ChannelGroup_Get3DCustomRolloff, self, ptr = int_ptr, nil)
|
289
|
+
buffer = Pointer.new(ptr.unpack1('J'), count * size).to_str
|
290
|
+
(0...count).map { |i| Vector.new(*buffer[i * size, size].unpack('fff')) }
|
291
|
+
end
|
292
|
+
|
293
|
+
def custom_rolloff=(rolloff)
|
294
|
+
FMOD.type?(rolloff, Array)
|
295
|
+
vectors = rolloff.map { |vector| vector.to_str }.join
|
296
|
+
FMOD.invoke(:ChannelGroup_Set3DCustomRolloff, self, vectors, rolloff.size)
|
297
|
+
rolloff
|
298
|
+
end
|
299
|
+
|
300
|
+
##
|
301
|
+
# Sets the speaker volume levels for each speaker individually, this is a
|
302
|
+
# helper to avoid having to set the mix matrix.
|
303
|
+
#
|
304
|
+
# Levels can be below 0 to invert a signal and above 1 to amplify the
|
305
|
+
# signal. Note that increasing the signal level too far may cause audible
|
306
|
+
# distortion. Speakers specified that don't exist will simply be ignored.
|
307
|
+
# For more advanced speaker control, including sending the different
|
308
|
+
# channels of a stereo sound to arbitrary speakers, see {#matrix}.
|
309
|
+
#
|
310
|
+
# @note This function overwrites any pan/mix-level by overwriting the
|
311
|
+
# {ChannelControl}'s matrix.
|
312
|
+
#
|
313
|
+
# @param fl [Float] Volume level for the front left speaker of a
|
314
|
+
# multichannel speaker setup, 0.0 (silent), 1.0 (normal volume).
|
315
|
+
# @param fr [Float] Volume level for the front right speaker of a
|
316
|
+
# multichannel speaker setup, 0.0 (silent), 1.0 (normal volume).
|
317
|
+
# @param center [Float] Volume level for the center speaker of a
|
318
|
+
# multichannel speaker setup, 0.0 (silent), 1.0 (normal volume).
|
319
|
+
# @param lfe [Float] Volume level for the sub-woofer speaker of a
|
320
|
+
# multichannel speaker setup, 0.0 (silent), 1.0 (normal volume).
|
321
|
+
# @param sl [Float] Volume level for the surround left speaker of a
|
322
|
+
# multichannel speaker setup, 0.0 (silent), 1.0 (normal volume).
|
323
|
+
# @param sr [Float] Volume level for the surround right speaker of a
|
324
|
+
# multichannel speaker setup, 0.0 (silent), 1.0 (normal volume).
|
325
|
+
# @param bl [Float] Volume level for the back left speaker of a multichannel
|
326
|
+
# speaker setup, 0.0 (silent), 1.0 (normal volume).
|
327
|
+
# @param br [Float] Volume level for the back right speaker of a
|
328
|
+
# multichannel speaker setup, 0.0 (silent), 1.0 (normal volume).
|
329
|
+
# @return [self]
|
330
|
+
def output_mix(fl, fr, center, lfe, sl, sr, bl, br)
|
331
|
+
FMOD.invoke(:ChannelGroup_SetMixLevelsOutput, self, fl,
|
332
|
+
fr, center, lfe, sl, sr, bl, br)
|
333
|
+
self
|
334
|
+
end
|
335
|
+
|
336
|
+
##
|
337
|
+
# Sets the incoming volume level for each channel of a multi-channel sound.
|
338
|
+
# This is a helper to avoid calling {#matrix}.
|
339
|
+
#
|
340
|
+
# A multi-channel sound is a single sound that contains from 1 to 32
|
341
|
+
# channels of sound data, in an interleaved fashion. If in the extreme case,
|
342
|
+
# a 32 channel wave file was used, an array of 32 floating point numbers
|
343
|
+
# denoting their volume levels would be passed in to the levels parameter.
|
344
|
+
#
|
345
|
+
# @param levels [Array<Float>] Array of volume levels for each incoming
|
346
|
+
# channel.
|
347
|
+
# @return [self]
|
348
|
+
def input_mix(*levels)
|
349
|
+
count = levels.size
|
350
|
+
binary = levels.pack('f*')
|
351
|
+
FMOD.invoke(:ChannelGroup_SetMixLevelsInput, self, binary, count)
|
352
|
+
self
|
353
|
+
end
|
354
|
+
|
355
|
+
##
|
356
|
+
# @!attribute min_distance
|
357
|
+
# @return [Float] Minimum volume distance in "units". (Default: 1.0)
|
358
|
+
# @see max_distance
|
359
|
+
# @see min_max_distance
|
360
|
+
|
361
|
+
def min_distance
|
362
|
+
min = "\0" * SIZEOF_FLOAT
|
363
|
+
FMOD.invoke(:ChannelGroup_Get3DMinMaxDistance, self, min, nil)
|
364
|
+
min.unpack1('f')
|
365
|
+
end
|
366
|
+
|
367
|
+
def min_distance=(distance)
|
368
|
+
min_max_distance(distance, max_distance)
|
369
|
+
end
|
370
|
+
|
371
|
+
##
|
372
|
+
# @!attribute max_distance
|
373
|
+
# @return [Float] Maximum volume distance in "units". (Default: 10000.0)
|
374
|
+
# @see min_distance
|
375
|
+
# @see in_max_distance
|
376
|
+
|
377
|
+
def max_distance
|
378
|
+
max = "\0" * SIZEOF_FLOAT
|
379
|
+
FMOD.invoke(:ChannelGroup_Get3DMinMaxDistance, self, nil, max)
|
380
|
+
max.unpack1('f')
|
381
|
+
end
|
382
|
+
|
383
|
+
def max_distance=(distance)
|
384
|
+
min_max_distance(min_distance, distance)
|
385
|
+
end
|
386
|
+
|
387
|
+
##
|
388
|
+
# Sets the minimum and maximum audible distance.
|
389
|
+
#
|
390
|
+
# When the listener is in-between the minimum distance and the sound source
|
391
|
+
# the volume will be at its maximum. As the listener moves from the minimum
|
392
|
+
# distance to the maximum distance the sound will attenuate following the
|
393
|
+
# rolloff curve set. When outside the maximum distance the sound will no
|
394
|
+
# longer attenuate.
|
395
|
+
#
|
396
|
+
# Minimum distance is useful to give the impression that the sound is loud
|
397
|
+
# or soft in 3D space. An example of this is a small quiet object, such as a
|
398
|
+
# bumblebee, which you could set a small minimum distance such as 0.1. This
|
399
|
+
# would cause it to attenuate quickly and disappear when only a few meters
|
400
|
+
# away from the listener. Another example is a jumbo jet, which you could
|
401
|
+
# set to a minimum distance of 100.0 causing the volume to stay at its
|
402
|
+
# loudest until the listener was 100 meters away, then it would be hundreds
|
403
|
+
# of meters more before it would fade out.
|
404
|
+
#
|
405
|
+
# Maximum distance is effectively obsolete unless you need the sound to stop
|
406
|
+
# fading out at a certain point. Do not adjust this from the default if you
|
407
|
+
# dont need to. Some people have the confusion that maximum distance is the
|
408
|
+
# point the sound will fade out to zero, this is not the case.
|
409
|
+
#
|
410
|
+
# @param min [Float] Minimum volume distance in "units".
|
411
|
+
# * *Default:* 1.0
|
412
|
+
# @param max [Float] Maximum volume distance in "units".
|
413
|
+
# * *Default:* 10000.0
|
414
|
+
#
|
415
|
+
# @see min_distance
|
416
|
+
# @see max_distance
|
417
|
+
def min_max_distance(min, max)
|
418
|
+
FMOD.invoke(:ChannelGroup_Set3DMinMaxDistance, self, min, max)
|
419
|
+
end
|
420
|
+
|
421
|
+
|
422
|
+
##
|
423
|
+
# @!attribute matrix
|
424
|
+
# A 2D pan matrix that maps input channels (columns) to output speakers
|
425
|
+
# (rows).
|
426
|
+
#
|
427
|
+
# Levels can be below 0 to invert a signal and above 1 to amplify the
|
428
|
+
# signal. Note that increasing the signal level too far may cause audible
|
429
|
+
# distortion.
|
430
|
+
#
|
431
|
+
# The matrix size will generally be the size of the number of channels in
|
432
|
+
# the current speaker mode. Use {System.software_format }to determine this.
|
433
|
+
#
|
434
|
+
# If a matrix already exists then the matrix passed in will applied over the
|
435
|
+
# top of it. The input matrix can be smaller than the existing matrix.
|
436
|
+
#
|
437
|
+
# A "unit" matrix allows a signal to pass through unchanged. For example for
|
438
|
+
# a 5.1 matrix a unit matrix would look like this:
|
439
|
+
# [[ 1, 0, 0, 0, 0, 0 ]
|
440
|
+
# [ 0, 1, 0, 0, 0, 0 ]
|
441
|
+
# [ 0, 0, 1, 0, 0, 0 ]
|
442
|
+
# [ 0, 0, 0, 1, 0, 0 ]
|
443
|
+
# [ 0, 0, 0, 0, 1, 0 ]
|
444
|
+
# [ 0, 0, 0, 0, 0, 1 ]]
|
445
|
+
#
|
446
|
+
# @return [Array<Array<Float>>] a 2-dimensional array of volume levels in
|
447
|
+
# row-major order. Each row represents an output speaker, each column
|
448
|
+
# represents an input channel.
|
449
|
+
def matrix
|
450
|
+
o, i = "\0" * SIZEOF_INT, "\0" * SIZEOF_INT
|
451
|
+
FMOD.invoke(:ChannelGroup_GetMixMatrix, self, nil, o, i, 0)
|
452
|
+
o, i = o.unpack1('l'), i.unpack1('l')
|
453
|
+
return [] if o.zero? || i.zero?
|
454
|
+
buffer = "\0" * (SIZEOF_FLOAT * o * i)
|
455
|
+
FMOD.invoke(:ChannelGroup_GetMixMatrix, self, buffer, int_ptr, int_ptr, 0)
|
456
|
+
buffer.unpack('f*').each_slice(i).to_a
|
457
|
+
end
|
458
|
+
|
459
|
+
def matrix=(matrix)
|
460
|
+
out_count, in_count = matrix.size, matrix.first.size
|
461
|
+
unless matrix.all? { |ary| ary.size == in_count }
|
462
|
+
raise Error, "Matrix contains unequal length input channels."
|
463
|
+
end
|
464
|
+
data = matrix.flatten.pack('f*')
|
465
|
+
FMOD.invoke(:ChannelGroup_SetMixMatrix, self, data,
|
466
|
+
out_count, in_count, 0)
|
467
|
+
end
|
468
|
+
|
469
|
+
|
470
|
+
def pan(pan)
|
471
|
+
FMOD.invoke(:ChannelGroup_SetPan, self, pan.clamp(-1.0, 1.0))
|
472
|
+
self
|
473
|
+
end
|
474
|
+
|
475
|
+
def stop
|
476
|
+
FMOD.invoke(:ChannelGroup_Stop, self)
|
477
|
+
self
|
478
|
+
end
|
479
|
+
|
480
|
+
def pause
|
481
|
+
FMOD.invoke(:ChannelGroup_SetPaused, self, 1)
|
482
|
+
self
|
483
|
+
end
|
484
|
+
|
485
|
+
def resume
|
486
|
+
FMOD.invoke(:ChannelGroup_SetPaused, self, 0)
|
487
|
+
self
|
488
|
+
end
|
489
|
+
|
490
|
+
def mute
|
491
|
+
FMOD.invoke(:ChannelGroup_SetMute, self, 1)
|
492
|
+
end
|
493
|
+
|
494
|
+
def unmute
|
495
|
+
FMOD.invoke(:ChannelGroup_SetMute, self, 0)
|
496
|
+
end
|
497
|
+
|
498
|
+
def dsps
|
499
|
+
DspChain.send(:new, self)
|
500
|
+
end
|
501
|
+
|
502
|
+
def parent
|
503
|
+
FMOD.invoke(:ChannelGroup_GetSystemObject, self, system = int_ptr)
|
504
|
+
System.new(system)
|
505
|
+
end
|
506
|
+
|
507
|
+
def fade_ramp(clock, volume)
|
508
|
+
FMOD.invoke(:ChannelGroup_SetFadePointRamp, self, clock, volume)
|
509
|
+
end
|
510
|
+
|
511
|
+
def dsp_clock
|
512
|
+
buffer = "\0" * SIZEOF_LONG_LONG
|
513
|
+
FMOD.invoke(:ChannelGroup_GetDSPClock, self, buffer, nil)
|
514
|
+
buffer.unpack1('Q')
|
515
|
+
end
|
516
|
+
|
517
|
+
def parent_clock
|
518
|
+
buffer = "\0" * SIZEOF_LONG_LONG
|
519
|
+
FMOD.invoke(:ChannelGroup_GetDSPClock, self, nil, buffer)
|
520
|
+
buffer.unpack1('Q')
|
521
|
+
end
|
522
|
+
|
523
|
+
def get_reverb_level(index)
|
524
|
+
wet = "\0" * SIZEOF_FLOAT
|
525
|
+
FMOD.invoke(:ChannelGroup_GetReverbProperties, self, index, wet)
|
526
|
+
wet.unpack1('f')
|
527
|
+
end
|
528
|
+
|
529
|
+
def set_reverb_level(index, wet_level)
|
530
|
+
wet = wet_level.clamp(0.0, 1.0)
|
531
|
+
FMOD.invoke(:ChannelGroup_SetReverbProperties, self, index, wet)
|
532
|
+
wet
|
533
|
+
end
|
534
|
+
|
535
|
+
def delay
|
536
|
+
clock_start = "\0" * SIZEOF_LONG_LONG
|
537
|
+
clock_end = "\0" * SIZEOF_LONG_LONG
|
538
|
+
stop = "\0" * SIZEOF_INT
|
539
|
+
FMOD.invoke(:ChannelGroup_GetDelay, self, clock_start, clock_end, stop)
|
540
|
+
stop = stop.unpack1('l') != 0
|
541
|
+
ChannelDelay.new(clock_start.unpack1('Q'), clock_end.unpack1('Q'), stop)
|
542
|
+
end
|
543
|
+
|
544
|
+
def delay=(delay)
|
545
|
+
FMOD.type?(delay, ChannelDelay)
|
546
|
+
set_delay(delay.start, delay.end, delay.stop)
|
547
|
+
delay
|
548
|
+
end
|
549
|
+
|
550
|
+
def set_delay(clock_start, clock_end, stop)
|
551
|
+
stop = stop.to_i
|
552
|
+
FMOD.invoke(:ChannelGroup_SetDelay, self, clock_start, clock_end, stop)
|
553
|
+
self
|
554
|
+
end
|
555
|
+
|
556
|
+
def cone_orientation
|
557
|
+
vector = FMOD::Core::Vector.new
|
558
|
+
FMOD.invoke(:ChannelGroup_Get3DConeOrientation, self, vector)
|
559
|
+
vector
|
560
|
+
end
|
561
|
+
|
562
|
+
def cone_orientation=(vector)
|
563
|
+
FMOD.type?(vector, Vector)
|
564
|
+
FMOD.invoke(:ChannelGroup_Set3DConeOrientation, self, vector)
|
565
|
+
vector
|
566
|
+
end
|
567
|
+
|
568
|
+
# @!group Callbacks
|
569
|
+
|
570
|
+
##
|
571
|
+
# Binds the given block so that it will be invoked when the channel is
|
572
|
+
# stopped, either by {#stop} or when playback reaches an end.
|
573
|
+
# @example
|
574
|
+
# >> channel.on_stop do
|
575
|
+
# >> puts "Channel stop"
|
576
|
+
# >> end
|
577
|
+
# >> channel.stop
|
578
|
+
#
|
579
|
+
# "Channel stop"
|
580
|
+
# @param proc [Proc] Proc to call. Optional, must give block if nil.
|
581
|
+
# @yield The block to call when the {ChannelControl} is stopped.
|
582
|
+
# @yieldreturn [Channel] The {ChannelControl} receiving this callback.
|
583
|
+
# @return [self]
|
584
|
+
def on_stop(proc = nil, &block)
|
585
|
+
set_callback(0, &(block_given? ? block : proc))
|
586
|
+
end
|
587
|
+
|
588
|
+
##
|
589
|
+
# Binds the given block so that it will be invoked when the a voice is
|
590
|
+
# swapped to or from emulated/real.
|
591
|
+
# @param proc [Proc] Proc to call. Optional, must give block if nil.
|
592
|
+
# @yield [emulated] The block to call when a voice is swapped, with flag
|
593
|
+
# indicating if voice is emulated passed to it.
|
594
|
+
# @yieldparam emulated [Boolean]
|
595
|
+
# * *true:* Swapped from real to emulated
|
596
|
+
# * *false:* Swapped from emulated to real
|
597
|
+
# @yieldreturn [Channel] The {ChannelControl} receiving this callback.
|
598
|
+
# @return [self]
|
599
|
+
def on_voice_swap(proc = nil, &block)
|
600
|
+
set_callback(1, &(block_given? ? block : proc))
|
601
|
+
end
|
602
|
+
|
603
|
+
##
|
604
|
+
# Binds the given block so that it will be invoked when a sync-point is
|
605
|
+
# encountered.
|
606
|
+
# @param proc [Proc] Proc to call. Optional, must give block if nil.
|
607
|
+
# @yield [index] The block to call when a sync-point is encountered, with
|
608
|
+
# the index of the sync-point passed to it.
|
609
|
+
# @yieldparam index [Integer] The sync-point index.
|
610
|
+
# @yieldreturn [Channel] The {ChannelControl} receiving this callback.
|
611
|
+
# @return [self]
|
612
|
+
def on_sync_point(proc = nil, &block)
|
613
|
+
set_callback(2, &(block_given? ? block : proc))
|
614
|
+
end
|
615
|
+
|
616
|
+
##
|
617
|
+
# Binds the given block so that it will be invoked when the occlusion is
|
618
|
+
# calculated.
|
619
|
+
# @param proc [Proc] Proc to call. Optional, must give block if nil.
|
620
|
+
# @yield [direct, reverb] The block to call when occlusion is calculated,
|
621
|
+
# with pointers to the direct and reverb occlusion values passed to it.
|
622
|
+
# @yieldparam direct [Pointer] A pointer to a floating point direct value
|
623
|
+
# that can be read (de-referenced) and modified after the geometry engine
|
624
|
+
# has calculated it for this channel.
|
625
|
+
# @yieldparam reverb [Pointer] A pointer to a floating point reverb value
|
626
|
+
# that can be read (de-referenced) and modified after the geometry engine
|
627
|
+
# has calculated it for this channel.
|
628
|
+
# @yieldreturn [Channel] The {ChannelControl} receiving this callback.
|
629
|
+
# @return [self]
|
630
|
+
def on_occlusion(proc = nil, &block)
|
631
|
+
set_callback(3, &(block_given? ? block : proc))
|
632
|
+
end
|
633
|
+
|
634
|
+
# @!endgroup
|
635
|
+
|
636
|
+
def initialize(address = nil)
|
637
|
+
super
|
638
|
+
@callbacks = {}
|
639
|
+
ret = TYPE_INT
|
640
|
+
sig = [TYPE_VOIDP, TYPE_INT, TYPE_INT, TYPE_VOIDP, TYPE_VOIDP]
|
641
|
+
abi = FMOD::ABI
|
642
|
+
bc = Closure::BlockCaller.new(ret, sig, abi) do |_c, _t, cb_type, d1, d2|
|
643
|
+
if @callbacks[cb_type]
|
644
|
+
case cb_type
|
645
|
+
when 0 then @callbacks[0].each(&:call)
|
646
|
+
when 1
|
647
|
+
virtual = d1.to_s(SIZEOF_INT).unpack1('l') != 0
|
648
|
+
@callbacks[1].each { |cb| cb.call(virtual) }
|
649
|
+
when 2
|
650
|
+
index = d1.to_s(SIZEOF_INT).unpack1('l')
|
651
|
+
@callbacks[2].each { |cb| cb.call(index) }
|
652
|
+
when 3 then @callbacks[3].each { |cb| cb.call(d1, d2) }
|
653
|
+
else raise FMOD::Error, "Invalid channel callback type."
|
654
|
+
end
|
655
|
+
end
|
656
|
+
Result::OK
|
657
|
+
end
|
658
|
+
FMOD.invoke(:ChannelGroup_SetCallback, self, bc)
|
659
|
+
end
|
660
|
+
|
661
|
+
private
|
662
|
+
|
663
|
+
def set_callback(index, &block)
|
664
|
+
raise LocalJumpError, "No block given." unless block_given?
|
665
|
+
@callbacks[index] ||= []
|
666
|
+
@callbacks[index] << block
|
667
|
+
self
|
668
|
+
end
|
669
|
+
|
670
|
+
##
|
671
|
+
# Emulates an Array-type container of a {ChannelControl}'s DSP chain.
|
672
|
+
class DspChain
|
673
|
+
|
674
|
+
include Enumerable
|
675
|
+
|
676
|
+
##
|
677
|
+
# Creates a new instance of a {DspChain} for the specified
|
678
|
+
# {ChannelControl}.
|
679
|
+
#
|
680
|
+
# @param channel [ChannelControl] The channel or channel group to create
|
681
|
+
# the collection wrapper for.
|
682
|
+
def initialize(channel)
|
683
|
+
FMOD.type?(channel, ChannelControl)
|
684
|
+
@channel = channel
|
685
|
+
end
|
686
|
+
|
687
|
+
##
|
688
|
+
# Retrieves the number of DSPs within the chain. This includes the
|
689
|
+
# built-in {FMOD::Effects::Fader} DSP.
|
690
|
+
# @return [Integer]
|
691
|
+
def count
|
692
|
+
buffer = "\0" * Fiddle::SIZEOF_INT
|
693
|
+
FMOD.invoke(:ChannelGroup_GetNumDSPs, @channel, buffer)
|
694
|
+
buffer.unpack1('l')
|
695
|
+
end
|
696
|
+
|
697
|
+
##
|
698
|
+
# @overload each(&block)
|
699
|
+
# If called with a block, passes each DSP in turn before returning self.
|
700
|
+
# @yield [dsp] Yields a DSP instance to the block.
|
701
|
+
# @yieldparam dsp [Dsp] The DSP instance.
|
702
|
+
# @return [self]
|
703
|
+
# @overload each
|
704
|
+
# Returns an enumerator for the {DspChain} if no block is given.
|
705
|
+
# @return [Enumerator]
|
706
|
+
def each
|
707
|
+
return to_enum(:each) unless block_given?
|
708
|
+
(0...count).each { |i| yield self[i] }
|
709
|
+
self
|
710
|
+
end
|
711
|
+
|
712
|
+
##
|
713
|
+
# Element reference. Returns the element at index.
|
714
|
+
# @param index [Integer] The index into the {DspChain} to retrieve.
|
715
|
+
# @return [Dsp|nil] The DSP at the specified index, or +nil+ if index is
|
716
|
+
# out of range.
|
717
|
+
def [](index)
|
718
|
+
return nil unless index.between?(-2, count)
|
719
|
+
dsp = "\0" * Fiddle::SIZEOF_INTPTR_T
|
720
|
+
FMOD.invoke(:ChannelGroup_GetDSP, @channel, index, dsp)
|
721
|
+
Dsp.from_handle(dsp)
|
722
|
+
end
|
723
|
+
|
724
|
+
##
|
725
|
+
# Element assignment. Sets the element at the specified index.
|
726
|
+
# @param index [Integer] The index into the {DspChain} to set.
|
727
|
+
# @param dsp [Dsp] A DSP instance.
|
728
|
+
# @return [Dsp] The given DSP instance.
|
729
|
+
def []=(index, dsp)
|
730
|
+
FMOD.type?(dsp, Dsp)
|
731
|
+
FMOD.invoke(:ChannelGroup_AddDSP, @channel, index, dsp)
|
732
|
+
dsp
|
733
|
+
end
|
734
|
+
|
735
|
+
##
|
736
|
+
# Appends or pushes the given object(s) on to the end of this {DspChain}. This
|
737
|
+
# expression returns +self+, so several appends may be chained together.
|
738
|
+
# @param dsp [Dsp] One or more DSP instance(s).
|
739
|
+
# @return [self]
|
740
|
+
def add(*dsp)
|
741
|
+
dsp.each { |d| self[DspIndex::TAIL] = d }
|
742
|
+
self
|
743
|
+
end
|
744
|
+
|
745
|
+
##
|
746
|
+
# Prepends objects to the front of +self+, moving other elements upwards.
|
747
|
+
# @param dsp [Dsp] A DSP instance.
|
748
|
+
# @return [self]
|
749
|
+
def unshift(dsp)
|
750
|
+
self[DspIndex::HEAD] = dsp
|
751
|
+
self
|
752
|
+
end
|
753
|
+
|
754
|
+
##
|
755
|
+
# Removes the last element from +self+ and returns it, or +nil+ if the
|
756
|
+
# {DspChain} is empty.
|
757
|
+
# @return [Dsp|nil]
|
758
|
+
def pop
|
759
|
+
dsp = self[DspIndex::TAIL]
|
760
|
+
remove(dsp)
|
761
|
+
dsp
|
762
|
+
end
|
763
|
+
|
764
|
+
##
|
765
|
+
# Returns the first element of +self+ and removes it (shifting all other
|
766
|
+
# elements down by one). Returns +nil+ if the array is empty.
|
767
|
+
# @return [Dsp|nil]
|
768
|
+
def shift
|
769
|
+
dsp = self[DspIndex::HEAD]
|
770
|
+
remove(dsp)
|
771
|
+
dsp
|
772
|
+
end
|
773
|
+
|
774
|
+
##
|
775
|
+
# Deletes the specified DSP from this DSP chain. This does not release ot
|
776
|
+
# dispose the DSP unit, only removes from this {DspChain}, as a DSP unit
|
777
|
+
# can be shared.
|
778
|
+
# @param dsp [Dsp] The DSP to remove.
|
779
|
+
# @return [self]
|
780
|
+
def remove(dsp)
|
781
|
+
return unless dsp.is_a?(Dsp)
|
782
|
+
FMOD.invoke(:ChannelGroup_RemoveDSP, @channel, dsp)
|
783
|
+
self
|
784
|
+
end
|
785
|
+
|
786
|
+
##
|
787
|
+
# Returns the index of the specified DSP.
|
788
|
+
# @param dsp [Dsp] The DSP to retrieve the index of.
|
789
|
+
# @return [Integer] The index of the DSP.
|
790
|
+
def index(dsp)
|
791
|
+
FMOD.type?(dsp, Dsp)
|
792
|
+
buffer = "\0" * Fiddle::SIZEOF_INT
|
793
|
+
FMOD.invoke(:ChannelGroup_GetDSPIndex, @channel, dsp, buffer)
|
794
|
+
buffer.unpack1('l')
|
795
|
+
end
|
796
|
+
|
797
|
+
##
|
798
|
+
# Moves a DSP unit that exists in this {DspChain} to a new index.
|
799
|
+
# @param dsp [Dsp] The DSP instance to move, must exist within this
|
800
|
+
# {DspChain}.
|
801
|
+
# @param index [Integer] The new index to place the specified DSP.
|
802
|
+
# @return [self]
|
803
|
+
def move(dsp, index)
|
804
|
+
FMOD.type?(dsp, Dsp)
|
805
|
+
FMOD.invoke(:ChannelGroup_SetDSPIndex, @channel, dsp, index)
|
806
|
+
self
|
807
|
+
end
|
808
|
+
|
809
|
+
alias_method :size, :count
|
810
|
+
alias_method :length, :count
|
811
|
+
alias_method :length, :count
|
812
|
+
alias_method :delete, :remove
|
813
|
+
alias_method :push, :add
|
814
|
+
alias_method :<<, :add
|
815
|
+
|
816
|
+
private_class_method :new
|
817
|
+
end
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
|