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.
- 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
|
+
|