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/handle.rb
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
require 'fiddle'
|
|
2
|
+
require 'fiddle/import'
|
|
3
|
+
|
|
4
|
+
module FMOD
|
|
5
|
+
class Handle < Fiddle::Pointer
|
|
6
|
+
|
|
7
|
+
include Fiddle
|
|
8
|
+
include FMOD::Core
|
|
9
|
+
|
|
10
|
+
def initialize(address)
|
|
11
|
+
address = address.unpack1('J') if address.is_a?(String)
|
|
12
|
+
super(address.to_i)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def release
|
|
16
|
+
case self
|
|
17
|
+
when Sound then FMOD.invoke(:Sound_Release, self)
|
|
18
|
+
when Dsp then FMOD.invoke(:DSP_Release, self)
|
|
19
|
+
when ChannelGroup then FMOD.invoke(:ChannelGroup_Release, self)
|
|
20
|
+
when Geometry then FMOD.invoke(:Geometry_Release, self)
|
|
21
|
+
when Reverb3D then FMOD.invoke(:Reverb3D_Release, self)
|
|
22
|
+
when SoundGroup then FMOD.invoke(:SoundGroup_Release, self)
|
|
23
|
+
when System then FMOD.invoke(:System_Release, self)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
alias_method :dispose, :release
|
|
28
|
+
|
|
29
|
+
def user_data
|
|
30
|
+
pointer = int_ptr
|
|
31
|
+
case self
|
|
32
|
+
when ChannelControl
|
|
33
|
+
FMOD.invoke(:ChannelGroup_GetUserData, self, pointer)
|
|
34
|
+
when Dsp
|
|
35
|
+
FMOD.invoke(:DSP_GetUserData, self, pointer)
|
|
36
|
+
when DspConnection
|
|
37
|
+
FMOD.invoke(:DSPConnection_GetUserData, self, pointer)
|
|
38
|
+
when Geometry
|
|
39
|
+
FMOD.invoke(:Geometry_GetUserData, self, pointer)
|
|
40
|
+
when Reverb3D
|
|
41
|
+
FMOD.invoke(:Reverb3D_GetUserData, self, pointer)
|
|
42
|
+
when Sound
|
|
43
|
+
FMOD.invoke(:Sound_GetUserData, self, pointer)
|
|
44
|
+
when SoundGroup
|
|
45
|
+
FMOD.invoke(:SoundGroup_GetUserData, self, pointer)
|
|
46
|
+
when System
|
|
47
|
+
FMOD.invoke(:System_GetUserData, self, pointer)
|
|
48
|
+
end
|
|
49
|
+
Pointer.new(pointer.unpack1('J'))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def user_data=(pointer)
|
|
53
|
+
case self
|
|
54
|
+
when ChannelControl
|
|
55
|
+
FMOD.invoke(:ChannelGroup_SetUserData, self, pointer)
|
|
56
|
+
when Dsp
|
|
57
|
+
FMOD.invoke(:DSP_SetUserData, self, pointer)
|
|
58
|
+
when DspConnection
|
|
59
|
+
FMOD.invoke(:DSPConnection_SetUserData, self, pointer)
|
|
60
|
+
when Geometry
|
|
61
|
+
FMOD.invoke(:Geometry_SetUserData, self, pointer)
|
|
62
|
+
when Reverb3D
|
|
63
|
+
FMOD.invoke(:Reverb3D_SetUserData, self, pointer)
|
|
64
|
+
when Sound
|
|
65
|
+
FMOD.invoke(:Sound_SetUserData, self, pointer)
|
|
66
|
+
when SoundGroup
|
|
67
|
+
FMOD.invoke(:SoundGroup_SetUserData, self, pointer)
|
|
68
|
+
when System
|
|
69
|
+
FMOD.invoke(:System_SetUserData, self, pointer)
|
|
70
|
+
end
|
|
71
|
+
pointer
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def to_s
|
|
75
|
+
inspect # TODO
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def int_ptr
|
|
79
|
+
"\0" * SIZEOF_INTPTR_T
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def self.bool_reader(name, function)
|
|
85
|
+
self.send(:define_method, name) do
|
|
86
|
+
FMOD.invoke(function, self, buffer = "\0" * SIZEOF_INT)
|
|
87
|
+
buffer.unpack1('l') != 0
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def self.bool_writer(name, function)
|
|
92
|
+
self.send(:define_method, name) do |bool|
|
|
93
|
+
FMOD.invoke(function, self, bool.to_i)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def self.integer_reader(name, function)
|
|
98
|
+
self.send(:define_method, name) do
|
|
99
|
+
FMOD.invoke(function, self, buffer = "\0" * SIZEOF_INT)
|
|
100
|
+
buffer.unpack1('l')
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def self.integer_writer(name, function, min = nil, max = nil)
|
|
105
|
+
self.send(:define_method, name) do |int|
|
|
106
|
+
int = min if min && int < min
|
|
107
|
+
int = max if max && int > max
|
|
108
|
+
FMOD.invoke(function, self, int)
|
|
109
|
+
int
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def self.float_reader(name, function)
|
|
114
|
+
self.send(:define_method, name) do
|
|
115
|
+
FMOD.invoke(function, self, buffer = "\0" * SIZEOF_FLOAT)
|
|
116
|
+
buffer.unpack1('f')
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def self.float_writer(name, function, min = nil, max = nil)
|
|
121
|
+
self.send(:define_method, name) do |float|
|
|
122
|
+
float = min if min && float < min
|
|
123
|
+
float = max if max && float > max
|
|
124
|
+
FMOD.invoke(function, self, float)
|
|
125
|
+
float
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
|
|
2
|
+
module FMOD
|
|
3
|
+
|
|
4
|
+
##
|
|
5
|
+
# The 3D reverb object is a sphere having 3D attributes (position, minimum
|
|
6
|
+
# distance, maximum distance) and reverb properties.
|
|
7
|
+
#
|
|
8
|
+
# The properties and 3D attributes of all reverb objects collectively
|
|
9
|
+
# determine, along with the listener's position, the settings of and input
|
|
10
|
+
# gains into a single 3D reverb DSP.
|
|
11
|
+
#
|
|
12
|
+
# When the listener is within the sphere of effect of one or more 3D reverbs,
|
|
13
|
+
# the listener's 3D reverb properties are a weighted combination of such 3D
|
|
14
|
+
# reverbs. When the listener is outside all of the reverbs, the 3D reverb
|
|
15
|
+
# setting is set to the default ambient reverb setting.
|
|
16
|
+
class Reverb3D < Handle
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# @!attribute properties
|
|
20
|
+
# Gets or sets the reverb parameters for the current reverb object.
|
|
21
|
+
# @return [Reverb]
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
# @!attribute position
|
|
25
|
+
# A {Vector} containing the 3D position of the center of the reverb in 3D
|
|
26
|
+
# space.
|
|
27
|
+
# * *Default:* {Vector.zero}
|
|
28
|
+
# @return [Vector]
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
# @!attribute min_distance
|
|
32
|
+
# The distance from the center-point that the reverb will have full effect
|
|
33
|
+
# at.
|
|
34
|
+
# * *Default:* 0.0
|
|
35
|
+
# @return [Float]
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# @!attribute max_distance
|
|
39
|
+
# The distance from the center-point that the reverb will not have any
|
|
40
|
+
# effect.
|
|
41
|
+
# * *Default:* 0.0
|
|
42
|
+
# @return [Float]
|
|
43
|
+
|
|
44
|
+
##
|
|
45
|
+
# @!attribute active
|
|
46
|
+
# Gets or sets the a state to disable or enable a reverb object so that it
|
|
47
|
+
# does or does not contribute to the 3D scene.
|
|
48
|
+
#
|
|
49
|
+
# @return [Boolean]
|
|
50
|
+
bool_reader(:active, :Reverb3D_GetActive)
|
|
51
|
+
bool_writer(:active=, :Reverb3D_SetActive)
|
|
52
|
+
|
|
53
|
+
def min_distance
|
|
54
|
+
buffer = "\0" * SIZEOF_FLOAT
|
|
55
|
+
FMOD.invoke(:Reverb3D_Get3DAttributes, self, nil, buffer, nil)
|
|
56
|
+
buffer.unpack1('f')
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def min_distance=(distance)
|
|
60
|
+
FMOD.invoke(:Reverb3D_Set3DAttributes, self, position,
|
|
61
|
+
distance, max_distance )
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def max_distance
|
|
65
|
+
buffer = "\0" * SIZEOF_FLOAT
|
|
66
|
+
FMOD.invoke(:Reverb3D_Get3DAttributes, self, nil, nil, buffer)
|
|
67
|
+
buffer.unpack1('f')
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def max_distance=(distance)
|
|
71
|
+
FMOD.invoke(:Reverb3D_Set3DAttributes, self, position,
|
|
72
|
+
min_distance, distance )
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def position
|
|
76
|
+
vector = Vector.zero
|
|
77
|
+
FMOD.invoke(:Reverb3D_Get3DAttributes, self, vector, nil, nil)
|
|
78
|
+
vector
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def position=(vector)
|
|
82
|
+
FMOD.type?(vector, Vector)
|
|
83
|
+
FMOD.invoke(:Reverb3D_Set3DAttributes, self, vector,
|
|
84
|
+
min_distance, max_distance )
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def properties
|
|
88
|
+
FMOD.invoke(:Reverb3D_GetProperties, self, reverb = Reverb.new)
|
|
89
|
+
reverb
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def properties=(reverb)
|
|
93
|
+
FMOD.type?(reverb, Reverb)
|
|
94
|
+
FMOD.invoke(:Reverb3D_SetProperties, self, reverb)
|
|
95
|
+
reverb
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
data/lib/fmod/sound.rb
ADDED
|
@@ -0,0 +1,810 @@
|
|
|
1
|
+
|
|
2
|
+
module FMOD
|
|
3
|
+
class Sound < Handle
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
# Contains format information about the sound.
|
|
7
|
+
# @attr type [Integer] The type of sound.
|
|
8
|
+
# @see SoundType
|
|
9
|
+
# @attr format [Integer] The format of the sound.
|
|
10
|
+
# @see SoundFormat
|
|
11
|
+
# @attr channels [Integer] The number of channels for the sound.
|
|
12
|
+
# @attr bits [Integer] The number of bits per sample for the sound.
|
|
13
|
+
Format = Struct.new(:type, :format, :channels, :bits)
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
# Contains the state a sound is in after {Mode::NON_BLOCKING} has been used
|
|
17
|
+
# to open it, or the state of the streaming buffer.
|
|
18
|
+
#
|
|
19
|
+
# When a sound is opened with {Mode::NON_BLOCKING}, it is opened and
|
|
20
|
+
# prepared in the background, or asynchronously.
|
|
21
|
+
# This allows the main application to execute without stalling on audio
|
|
22
|
+
# loads.
|
|
23
|
+
# This function will describe the state of the asynchronous load routine
|
|
24
|
+
# i.e. whether it has succeeded, failed or is still in progress.
|
|
25
|
+
#
|
|
26
|
+
# If {#starving?} is +true+, then you will most likely hear a
|
|
27
|
+
# stuttering/repeating sound as the decode buffer loops on itself and
|
|
28
|
+
# replays old data.
|
|
29
|
+
# You can detect buffer under-run and use something like
|
|
30
|
+
# {ChannelControl.mute} to keep it quiet until it is not starving any more.
|
|
31
|
+
class OpenState
|
|
32
|
+
|
|
33
|
+
private_class_method :new
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
# Opened and ready to play.
|
|
37
|
+
READY = 0
|
|
38
|
+
##
|
|
39
|
+
# Initial load in progress.
|
|
40
|
+
LOADING = 1
|
|
41
|
+
##
|
|
42
|
+
# Failed to open - file not found, out of memory etc.
|
|
43
|
+
ERROR = 2
|
|
44
|
+
##
|
|
45
|
+
# Connecting to remote host (internet sounds only).
|
|
46
|
+
CONNECTING = 3
|
|
47
|
+
##
|
|
48
|
+
# Buffering data.
|
|
49
|
+
BUFFERING = 4
|
|
50
|
+
##
|
|
51
|
+
# Seeking to subsound and re-flushing stream buffer.
|
|
52
|
+
SEEKING = 5
|
|
53
|
+
##
|
|
54
|
+
# Ready and playing, but not possible to release at this time without
|
|
55
|
+
# stalling the main thread.
|
|
56
|
+
PLAYING = 6
|
|
57
|
+
##
|
|
58
|
+
# Seeking within a stream to a different position.
|
|
59
|
+
SET_POSITION = 7
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# @return [Integer] the open state of a sound.
|
|
63
|
+
#
|
|
64
|
+
# Will be one of the following:
|
|
65
|
+
# * {READY}
|
|
66
|
+
# * {LOADING}
|
|
67
|
+
# * {ERROR}
|
|
68
|
+
# * {CONNECTING}
|
|
69
|
+
# * {BUFFERING}
|
|
70
|
+
# * {SEEKING}
|
|
71
|
+
# * {PLAYING}
|
|
72
|
+
# * {SET_POSITION}
|
|
73
|
+
attr_reader :state
|
|
74
|
+
|
|
75
|
+
##
|
|
76
|
+
# @return [Float] the percentage of the file buffer filled progress of a
|
|
77
|
+
# stream.
|
|
78
|
+
attr_reader :buffered
|
|
79
|
+
|
|
80
|
+
##
|
|
81
|
+
# The disk busy state of a sound.
|
|
82
|
+
# @return [Boolean] +true+ if disk is currently being accessed for the
|
|
83
|
+
# sound, otherwise +false+.
|
|
84
|
+
def busy?
|
|
85
|
+
@busy != 0
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
##
|
|
89
|
+
# The starving state of a sound.
|
|
90
|
+
# @return [Boolean] +true+ f a stream has decoded more than the stream
|
|
91
|
+
# file buffer has ready for it, otherwise +false+.
|
|
92
|
+
def starving?
|
|
93
|
+
@starving != 0
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# @api private
|
|
97
|
+
def initialize(state, buffered, busy, starving)
|
|
98
|
+
@state, @buffered, @busy, @starving = state, buffered, busy, starving
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
##
|
|
103
|
+
# @!attribute mode
|
|
104
|
+
# Sets or alters the mode of a sound.
|
|
105
|
+
#
|
|
106
|
+
# When calling this function, note that it will only take effect when the
|
|
107
|
+
# sound is played again with {#play}. Consider this mode the "default mode"
|
|
108
|
+
# for when the sound plays, not a mode that will suddenly change all
|
|
109
|
+
# currently playing instances of this sound.
|
|
110
|
+
#
|
|
111
|
+
# Supported flags:
|
|
112
|
+
# * {Mode::LOOP_OFF}
|
|
113
|
+
# * {Mode::LOOP_NORMAL}
|
|
114
|
+
# * {Mode::LOOP_BIDI}
|
|
115
|
+
# * {Mode::TWO_D}
|
|
116
|
+
# * {Mode::THREE_D}
|
|
117
|
+
# * {Mode::HEAD_RELATIVE_3D}
|
|
118
|
+
# * {Mode::WORLD_RELATIVE_3D}
|
|
119
|
+
# * {Mode::INVERSE_ROLLOFF_3D}
|
|
120
|
+
# * {Mode::LINEAR_ROLLOFF_3D}
|
|
121
|
+
# * {Mode::LINEAR_SQUARE_ROLLOFF_3D}
|
|
122
|
+
# * {Mode::CUSTOM_ROLLOFF_3D}
|
|
123
|
+
# * {Mode::IGNORE_GEOMETRY_3D}
|
|
124
|
+
#
|
|
125
|
+
# @return [Integer]
|
|
126
|
+
integer_reader(:mode, :Sound_GetMode)
|
|
127
|
+
integer_writer(:mode=, :Sound_SetMode)
|
|
128
|
+
|
|
129
|
+
##
|
|
130
|
+
# @!attribute loop_count
|
|
131
|
+
# Sets a sound, by default, to loop a specified number of times before
|
|
132
|
+
# stopping if its mode is set to {Mode::LOOP_NORMAL} or {Mode::LOOP_BIDI}.
|
|
133
|
+
# @return [Integer] the number of times to loop a sound before stopping.
|
|
134
|
+
integer_reader(:loop_count, :Sound_GetLoopCount)
|
|
135
|
+
integer_writer(:loop_count=, :Sound_SetLoopCount, -1)
|
|
136
|
+
|
|
137
|
+
##
|
|
138
|
+
# @!attribute [r] subsound_count
|
|
139
|
+
# @return [Integer] the number of subsounds stored within a sound.
|
|
140
|
+
integer_reader(:subsound_count, :Sound_GetNumSubSounds)
|
|
141
|
+
|
|
142
|
+
##
|
|
143
|
+
# @!attribute [r] syncpoint_count
|
|
144
|
+
# Retrieves the number of sync points stored within a sound. These points
|
|
145
|
+
# can be user generated or can come from a wav file with embedded markers.
|
|
146
|
+
# @return Retrieves the number of sync points stored within a sound.
|
|
147
|
+
integer_reader(:syncpoint_count, :Sound_GetNumSyncPoints)
|
|
148
|
+
|
|
149
|
+
##
|
|
150
|
+
# @!attribute [r] name
|
|
151
|
+
# @return [String] the name of the sound.
|
|
152
|
+
def name
|
|
153
|
+
FMOD.invoke(:Sound_GetName, self, buffer = "\0" * 512, 512)
|
|
154
|
+
buffer.delete("\0")
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
##
|
|
158
|
+
# @!attribute [r] tags
|
|
159
|
+
# @return [TagCollection] object containing the tags within the {Sound}.
|
|
160
|
+
def tags
|
|
161
|
+
TagCollection.send(:new, self)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
##
|
|
165
|
+
# @!attribute group
|
|
166
|
+
# @return [SoundGroup] the sound's current {SoundGroup}.
|
|
167
|
+
def group
|
|
168
|
+
FMOD.invoke(:Sound_GetSoundGroup, self, group = int_ptr)
|
|
169
|
+
SoundGroup.new(group)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def group=(group)
|
|
173
|
+
FMOD.type?(group, SoundGroup)
|
|
174
|
+
FMOD.invoke(:Sound_SetSoundGroup, self, group)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
##
|
|
178
|
+
# @!attribute [r] parent
|
|
179
|
+
# @return [System] the parent {System} object that was used to create this
|
|
180
|
+
# object.
|
|
181
|
+
def parent
|
|
182
|
+
FMOD.invoke(:Sound_GetSystemObject, self, system = int_ptr)
|
|
183
|
+
System.new(system)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
##
|
|
187
|
+
# Retrieves a handle to a {Sound} object that is contained within the parent
|
|
188
|
+
# sound.
|
|
189
|
+
# @param index [Integer] Index of the subsound to retrieve within this
|
|
190
|
+
# sound.
|
|
191
|
+
# @return [Sound, nil] the subsound, or +nil+
|
|
192
|
+
def subsound(index)
|
|
193
|
+
return nil unless index.between?(0, subsound_count - 1)
|
|
194
|
+
FMOD.invoke(:Sound_GetSubSound, self, index, sound = int_ptr)
|
|
195
|
+
Sound.new(sound)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
##
|
|
199
|
+
# @!attribute [r] subsounds
|
|
200
|
+
# @return [Array<Sound>] an array of the this sound's subsounds.
|
|
201
|
+
def subsounds
|
|
202
|
+
(0...subsound_count).map { |i| subsound(i) }
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
##
|
|
206
|
+
# Enumerates each subsound within this {Sound}.
|
|
207
|
+
# @overload each_subsound
|
|
208
|
+
# When block is given, yields each subsound before returning self.
|
|
209
|
+
# @yield [sound] Yields a sound to the block.
|
|
210
|
+
# @yieldparam sound [Sound] The current sound being enumerated.
|
|
211
|
+
# @return [self]
|
|
212
|
+
# @overload each_subsound
|
|
213
|
+
# When no block is given, returns an enumerator for the subsounds.
|
|
214
|
+
# @return [Enumerator]
|
|
215
|
+
def each_subsound
|
|
216
|
+
return to_enum(:each_subsound) unless block_given?
|
|
217
|
+
(0...subsound_count).each { |i| yield subsound(i) }
|
|
218
|
+
self
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
##
|
|
222
|
+
# @!attribute [r] parent_sound
|
|
223
|
+
# @return [Sound, nil] the parent {Sound} of this sound, or +nil+ if this
|
|
224
|
+
# sound is not a subsound.
|
|
225
|
+
def parent_sound
|
|
226
|
+
FMOD.invoke(:Sound_GetSubSoundParent, self, sound = "\0" * int_ptr)
|
|
227
|
+
address = sound.unpack1('J')
|
|
228
|
+
address.zero? ? nil : Sound.new(address)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
##
|
|
232
|
+
# Retrieve a handle to a sync point. These points can be user generated or
|
|
233
|
+
# can come from a wav file with embedded markers.
|
|
234
|
+
# @param index [Integer] Index of the sync point to retrieve.
|
|
235
|
+
# @return [Pointer] the sync point handle.
|
|
236
|
+
def syncpoint(index)
|
|
237
|
+
return nil unless index.between?(0, syncpoint_count - 1)
|
|
238
|
+
FMOD.invoke(:Sound_GetSyncPoint, self, index, sync = int_ptr)
|
|
239
|
+
Pointer.new(sync.unpack1('J'))
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
##
|
|
243
|
+
# Adds a sync point at a specific time within the sound. These points can be
|
|
244
|
+
# user generated or can come from a wav file with embedded markers.
|
|
245
|
+
# @param name [String] A name character string to be stored with the sync
|
|
246
|
+
# point.
|
|
247
|
+
# @param offset [Integer] Offset to add the callback sync-point for a sound.
|
|
248
|
+
# @param unit [Integer] Offset type to describe the offset provided.
|
|
249
|
+
# @see TimeUnit
|
|
250
|
+
# @return [Pointer] The sync point handle.
|
|
251
|
+
def add_syncpoint(name, offset, unit = TimeUnit::MS)
|
|
252
|
+
sync = int_ptr
|
|
253
|
+
FMOD.invoke(:Sound_AddSyncPoint, self, offset, unit, name, sync)
|
|
254
|
+
Pointer.new(sync.unpack1('J'))
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
##
|
|
258
|
+
# Deletes a syncpoint within the sound. These points can be user generated
|
|
259
|
+
# or can come from a wav file with embedded markers.
|
|
260
|
+
# @param sync_point [Pointer] A sync point handle.
|
|
261
|
+
# @return [void]
|
|
262
|
+
def delete_syncpoint(sync_point)
|
|
263
|
+
FMOD.type?(sync_point, Pointer)
|
|
264
|
+
FMOD.invoke(:Sound_DeleteSyncPoint, self, sync_point)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
##
|
|
268
|
+
# @!attribute [r] format
|
|
269
|
+
# @return [Format] the format information for the sound.
|
|
270
|
+
def format
|
|
271
|
+
arg = ["\0" * TYPE_INT, "\0" * TYPE_INT, "\0" * TYPE_INT, "\0" * TYPE_INT]
|
|
272
|
+
FMOD.invoke(:Sound_GetFormat, self, *arg)
|
|
273
|
+
arg.map! { |a| a.unpack1('l') }
|
|
274
|
+
Format.new(*arg)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# @!group Reading Sound Data
|
|
278
|
+
|
|
279
|
+
##
|
|
280
|
+
# Returns a pointer to the beginning of the sample data for a sound.
|
|
281
|
+
#
|
|
282
|
+
# With this function you get access to the RAW audio data, for example 8,
|
|
283
|
+
# 16, 24 or 32-bit PCM data, mono or stereo data. You must take this into
|
|
284
|
+
# consideration when processing the data within the pointer.
|
|
285
|
+
#
|
|
286
|
+
# @overload lock(offset, length)
|
|
287
|
+
# If called with a block, yields the pointers to the first and second
|
|
288
|
+
# sections of locked data before unlocking and returning +self+.
|
|
289
|
+
# @yield [ptr1, ptr2] Yields two pointers to the block.
|
|
290
|
+
# @yieldparam ptr1 [Pointer] Pointer to the first part of the locked data,
|
|
291
|
+
# and its size set to the number of locked bytes.
|
|
292
|
+
# @yieldparam ptr2 [Pointer] The second pointer will point to the second
|
|
293
|
+
# part of the locked data. This will be {FMOD::NULL} if the data locked
|
|
294
|
+
# hasn't wrapped at the end of the buffer, and its size will be 0.
|
|
295
|
+
# @return [self]
|
|
296
|
+
# @overload lock(offset, length)
|
|
297
|
+
# If called without a block, returns the pointers in an array, and
|
|
298
|
+
# {#unlock} must be called.
|
|
299
|
+
# @return [Array(Pointer, Pointer)] An array containing two pointers.
|
|
300
|
+
#
|
|
301
|
+
# The first pointer will point to the first part of the locked data, and
|
|
302
|
+
# its size set to the number of locked bytes.
|
|
303
|
+
#
|
|
304
|
+
# The second pointer will point to the second part of the locked data.
|
|
305
|
+
# This will be {FMOD::NULL} if the data locked hasn't wrapped at the end
|
|
306
|
+
# of the buffer, and its size will be 0.
|
|
307
|
+
#
|
|
308
|
+
# @param offset [Integer] Offset in bytes to the position to lock in the
|
|
309
|
+
# sample buffer.
|
|
310
|
+
# @param length [Integer] Number of bytes you want to lock in the sample
|
|
311
|
+
# buffer.
|
|
312
|
+
#
|
|
313
|
+
# @see unlock
|
|
314
|
+
def lock(offset, length)
|
|
315
|
+
p1, p2, s1, s2 = int_ptr, int_ptr, "\0" * TYPE_INT, "\0" * TYPE_INT
|
|
316
|
+
FMOD.invoke(:Sound_Lock, self, offset, length, p1, p2, s1, s2)
|
|
317
|
+
ptr1 = Pointer.new(p1.unpack1('J'), s1.unpack1('L'))
|
|
318
|
+
ptr2 = Pointer.new(p2.unpack1('J'), s2.unpack1('L'))
|
|
319
|
+
if block_given?
|
|
320
|
+
yield ptr1, ptr2
|
|
321
|
+
FMOD.invoke(:Sound_Unlock, self, ptr1, ptr2, ptr1.size, ptr2.size)
|
|
322
|
+
return self
|
|
323
|
+
end
|
|
324
|
+
[ptr1, ptr2]
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
##
|
|
328
|
+
# Releases previous sample data lock from {#lock}.
|
|
329
|
+
#
|
|
330
|
+
# @note This function should not be called if a block was passed to {#lock}.
|
|
331
|
+
#
|
|
332
|
+
# @param ptr1 [Pointer] Pointer to the first locked portion of sample data,
|
|
333
|
+
# from {#lock}.
|
|
334
|
+
# @param ptr2 [Pointer] Pointer to the second locked portion of sample data,
|
|
335
|
+
# from {#lock}.
|
|
336
|
+
# @see lock
|
|
337
|
+
# @return [void]
|
|
338
|
+
def unlock(ptr1, ptr2)
|
|
339
|
+
FMOD.invoke(:Sound_Unlock, self, ptr1, ptr2, ptr1.size, ptr2.size)
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
##
|
|
343
|
+
# Reads data from an opened sound to a buffer, using the FMOD codec created
|
|
344
|
+
# internally, and returns it.
|
|
345
|
+
#
|
|
346
|
+
# This can be used for decoding data offline in small pieces (or big
|
|
347
|
+
# pieces), rather than playing and capturing it, or loading the whole file
|
|
348
|
+
# at once and having to {#lock} / {#unlock} the data. If too much data is
|
|
349
|
+
# read, it is possible an EOF error will be thrown, meaning it is out of
|
|
350
|
+
# data. The "read" parameter will reflect this by returning a smaller number
|
|
351
|
+
# of bytes read than was requested. To avoid an error, simply compare the
|
|
352
|
+
# size of the returned buffer against what was requested before calling
|
|
353
|
+
# again.
|
|
354
|
+
#
|
|
355
|
+
# As a sound already reads the whole file then closes it upon calling
|
|
356
|
+
# {System.create_sound} (unless {System.create_stream} or
|
|
357
|
+
# {Mode::CREATE_STREAM} is used), this function will not work because the
|
|
358
|
+
# file is no longer open.
|
|
359
|
+
#
|
|
360
|
+
# Note that opening a stream makes it read a chunk of data and this will
|
|
361
|
+
# advance the read cursor. You need to either use {Mode::OPEN_ONLY} to stop
|
|
362
|
+
# the stream pre-buffering or call {#seek_data} to reset the read cursor.
|
|
363
|
+
#
|
|
364
|
+
# If {Mode::OPEN_ONLY} flag is used when opening a sound, it will leave the
|
|
365
|
+
# file handle open, and FMOD will not read any data internally, so the read
|
|
366
|
+
# cursor will be at position 0. This will allow the user to read the data
|
|
367
|
+
# from the start.
|
|
368
|
+
#
|
|
369
|
+
# As noted previously, if a sound is opened as a stream and this function is
|
|
370
|
+
# called to read some data, then you will 'miss the start' of the sound.
|
|
371
|
+
#
|
|
372
|
+
# {Channel.position} will have the same result. These function will flush
|
|
373
|
+
# the stream buffer and read in a chunk of audio internally. This is why if
|
|
374
|
+
# you want to read from an absolute position you should use Sound::seekData
|
|
375
|
+
# and not the previously mentioned functions.
|
|
376
|
+
#
|
|
377
|
+
# Remember if you are calling readData and seekData on a stream it is up to
|
|
378
|
+
# you to cope with the side effects that may occur. Information functions
|
|
379
|
+
# such as {Channel.position} may give misleading results. Calling
|
|
380
|
+
# {Channel.position} will reset and flush the stream, leading to the time
|
|
381
|
+
# values returning to their correct position.
|
|
382
|
+
#
|
|
383
|
+
# @param size [Integer] The number of bytes to read into the buffer.
|
|
384
|
+
# @return [String] A binary string containing the buffer data. The data will
|
|
385
|
+
# be the size specified unless it has reached the end of the data, in
|
|
386
|
+
# which case it will be less, and trimmed to length.
|
|
387
|
+
# @see seek_data
|
|
388
|
+
def read_data(size)
|
|
389
|
+
buffer = "\0" * size
|
|
390
|
+
read = "\0" * SIZEOF_INT
|
|
391
|
+
FMOD.invoke(:Sound_ReadData, self, buffer, size, read)
|
|
392
|
+
read = read.unpack1('L')
|
|
393
|
+
read < size ? buffer.byteslice(0, read) : buffer
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
##
|
|
397
|
+
# Seeks a sound for use with data reading.
|
|
398
|
+
#
|
|
399
|
+
# If a stream is opened and this function is called to read some data, then
|
|
400
|
+
# it will advance the internal file pointer, so data will be skipped if you
|
|
401
|
+
# play the stream. Also calling position / time information functions will
|
|
402
|
+
# lead to misleading results.
|
|
403
|
+
#
|
|
404
|
+
# A stream can be reset before playing by setting the position of the
|
|
405
|
+
# channel (ie using {Channel.position}), which will make it seek, reset and
|
|
406
|
+
# flush the stream buffer. This will make it sound correct again.
|
|
407
|
+
#
|
|
408
|
+
# Remember if you are calling {#read_data} and {#seek_data} on a stream it
|
|
409
|
+
# is up to you to cope with the side effects that may occur.
|
|
410
|
+
#
|
|
411
|
+
# @note This is not a function to "seek a sound" for normal use. This is for
|
|
412
|
+
# use in conjunction with {#read_data}.
|
|
413
|
+
# @param pcm [Integer] Offset to seek to in PCM samples.
|
|
414
|
+
# @return [void]
|
|
415
|
+
# @see read_data
|
|
416
|
+
def seek_data(pcm)
|
|
417
|
+
FMOD.invoke(:Sound_SeekData, self, pcm)
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# @!endgroup
|
|
421
|
+
|
|
422
|
+
# @!group 3-D Sound
|
|
423
|
+
|
|
424
|
+
##
|
|
425
|
+
# @!attribute cone_settings
|
|
426
|
+
# The angles that define the sound projection cone including the volume when
|
|
427
|
+
# outside the cone.
|
|
428
|
+
# @return [ConeSettings] the sound projection cone.
|
|
429
|
+
def cone_settings
|
|
430
|
+
args = ["\0" * SIZEOF_FLOAT, "\0" * SIZEOF_FLOAT, "\0" * SIZEOF_FLOAT]
|
|
431
|
+
FMOD.invoke(:Sound_Get3DConeSettings, self, *args)
|
|
432
|
+
ConeSettings.new(*args.map { |arg| arg.unpack1('f') } )
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def cone_settings=(settings)
|
|
436
|
+
FMOD.type?(settings, ConeSettings)
|
|
437
|
+
set_cone(*settings.values)
|
|
438
|
+
settings
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
##
|
|
442
|
+
# Sets the angles that define the sound projection cone including the volume
|
|
443
|
+
# when outside the cone.
|
|
444
|
+
# @param inside_angle [Float] Inside cone angle, in degrees. This is the
|
|
445
|
+
# angle within which the sound is at its normal volume.
|
|
446
|
+
# @param outside_angle [Float] Outside cone angle, in degrees. This is the
|
|
447
|
+
# angle outside of which the sound is at its outside volume.
|
|
448
|
+
# @param outside_volume [Float] Cone outside volume.
|
|
449
|
+
# @return [void]
|
|
450
|
+
def set_cone(inside_angle, outside_angle, outside_volume)
|
|
451
|
+
if outside_angle < inside_angle
|
|
452
|
+
raise Error, 'Outside angle must be greater than inside angle.'
|
|
453
|
+
end
|
|
454
|
+
FMOD.invoke(:Sound_Set3DConeSettings, self, inside_angle,
|
|
455
|
+
outside_angle, outside_volume)
|
|
456
|
+
self
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
##
|
|
460
|
+
# @!attribute custom_rolloff
|
|
461
|
+
# A custom rolloff curve to define how audio will attenuate over distance.
|
|
462
|
+
#
|
|
463
|
+
# Must be used in conjunction with {Mode::CUSTOM_ROLLOFF_3D} flag to be
|
|
464
|
+
# activated.
|
|
465
|
+
#
|
|
466
|
+
# <b>Points must be sorted by distance! Passing an unsorted list to FMOD
|
|
467
|
+
# will result in an error.</b>
|
|
468
|
+
# @return [Array<Vector>] the rolloff curve.
|
|
469
|
+
|
|
470
|
+
def custom_rolloff
|
|
471
|
+
count = "\0" * SIZEOF_INT
|
|
472
|
+
FMOD.invoke(:Sound_Get3DCustomRolloff, self, nil, count)
|
|
473
|
+
count = count.unpack1('l')
|
|
474
|
+
return [] if count.zero?
|
|
475
|
+
size = SIZEOF_FLOAT * 3
|
|
476
|
+
FMOD.invoke(:Sound_Get3DCustomRolloff, self, ptr = int_ptr, nil)
|
|
477
|
+
buffer = Pointer.new(ptr.unpack1('J'), count * size).to_str
|
|
478
|
+
(0...count).map { |i| Vector.new(*buffer[i * size, size].unpack('fff')) }
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
def custom_rolloff=(rolloff)
|
|
482
|
+
FMOD.type?(rolloff, Array)
|
|
483
|
+
vectors = rolloff.map { |vector| vector.to_str }.join
|
|
484
|
+
FMOD.invoke(:Sound_Set3DCustomRolloff, self, vectors, rolloff.size)
|
|
485
|
+
rolloff
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
##
|
|
489
|
+
# @!attribute min_distance
|
|
490
|
+
# @return [Float] Minimum volume distance in "units". (Default: 1.0)
|
|
491
|
+
# @see max_distance
|
|
492
|
+
# @see min_max_distance
|
|
493
|
+
|
|
494
|
+
def min_distance
|
|
495
|
+
min = "\0" * SIZEOF_FLOAT
|
|
496
|
+
FMOD.invoke(:Sound_Get3DMinMaxDistance, self, min, nil)
|
|
497
|
+
min.unpack1('f')
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
def min_distance=(distance)
|
|
501
|
+
min_max_distance(distance, max_distance)
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
##
|
|
505
|
+
# @!attribute max_distance
|
|
506
|
+
# @return [Float] Maximum volume distance in "units". (Default: 10000.0)
|
|
507
|
+
# @see min_distance
|
|
508
|
+
# @see in_max_distance
|
|
509
|
+
|
|
510
|
+
def max_distance
|
|
511
|
+
max = "\0" * SIZEOF_FLOAT
|
|
512
|
+
FMOD.invoke(:Sound_Get3DMinMaxDistance, self, nil, max)
|
|
513
|
+
max.unpack1('f')
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
def max_distance=(distance)
|
|
517
|
+
min_max_distance(min_distance, distance)
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
##
|
|
521
|
+
# Sets the minimum and maximum audible distance.
|
|
522
|
+
#
|
|
523
|
+
# When the listener is in-between the minimum distance and the sound source
|
|
524
|
+
# the volume will be at its maximum. As the listener moves from the minimum
|
|
525
|
+
# distance to the maximum distance the sound will attenuate following the
|
|
526
|
+
# rolloff curve set. When outside the maximum distance the sound will no
|
|
527
|
+
# longer attenuate.
|
|
528
|
+
#
|
|
529
|
+
# Minimum distance is useful to give the impression that the sound is loud
|
|
530
|
+
# or soft in 3D space. An example of this is a small quiet object, such as a
|
|
531
|
+
# bumblebee, which you could set a small minimum distance such as 0.1. This
|
|
532
|
+
# would cause it to attenuate quickly and disappear when only a few meters
|
|
533
|
+
# away from the listener. Another example is a jumbo jet, which you could
|
|
534
|
+
# set to a minimum distance of 100.0 causing the volume to stay at its
|
|
535
|
+
# loudest until the listener was 100 meters away, then it would be hundreds
|
|
536
|
+
# of meters more before it would fade out.
|
|
537
|
+
#
|
|
538
|
+
# Maximum distance is effectively obsolete unless you need the sound to stop
|
|
539
|
+
# fading out at a certain point. Do not adjust this from the default if you
|
|
540
|
+
# dont need to. Some people have the confusion that maximum distance is the
|
|
541
|
+
# point the sound will fade out to zero, this is not the case.
|
|
542
|
+
#
|
|
543
|
+
# @param min [Float] Minimum volume distance in "units".
|
|
544
|
+
# * *Default:* 1.0
|
|
545
|
+
# @param max [Float] Maximum volume distance in "units".
|
|
546
|
+
# * *Default:* 10000.0
|
|
547
|
+
#
|
|
548
|
+
# @see min_distance
|
|
549
|
+
# @see max_distance
|
|
550
|
+
# @return [void]
|
|
551
|
+
def min_max_distance(min, max)
|
|
552
|
+
FMOD.invoke(:Sound_Set3DMinMaxDistance, self, min, max)
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
# @!endgroup
|
|
556
|
+
|
|
557
|
+
##
|
|
558
|
+
# @!attribute default_frequency
|
|
559
|
+
# The sounds's default frequency, so when it is played it uses this value
|
|
560
|
+
# without having to specify them later for each channel each time the sound
|
|
561
|
+
# is played.
|
|
562
|
+
# @return [Float] the default playback frequency, in hz. (ie 44100hz).
|
|
563
|
+
|
|
564
|
+
def default_frequency
|
|
565
|
+
value = "\0" * SIZEOF_FLOAT
|
|
566
|
+
FMOD.invoke(:Sound_GetDefaults, self, value, nil)
|
|
567
|
+
value.unpack1('f')
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
def default_frequency=(frequency)
|
|
571
|
+
FMOD.invoke(:Sound_SetDefaults, self, frequency, default_priority)
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
##
|
|
575
|
+
# @!attribute default_priority
|
|
576
|
+
# The sounds's default priority, so when it is played it uses this value
|
|
577
|
+
# without having to specify them later for each channel each time the sound
|
|
578
|
+
# is played.
|
|
579
|
+
# @return [Integer] the default priority when played on a channel.
|
|
580
|
+
# * *Minimum:* 0 (most important)
|
|
581
|
+
# * *Maximum:* 256 (least important)
|
|
582
|
+
# * Default:* 128
|
|
583
|
+
|
|
584
|
+
def default_priority
|
|
585
|
+
value = "\0" * SIZEOF_INT
|
|
586
|
+
FMOD.invoke(:Sound_GetDefaults, self, nil, value)
|
|
587
|
+
value.unpack1('l')
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
def default_priority=(priority)
|
|
591
|
+
priority = priority.clamp(0, 256)
|
|
592
|
+
FMOD.invoke(:Sound_SetDefaults, self, default_frequency, priority)
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
##
|
|
596
|
+
# Retrieves the length of the sound using the specified time unit.
|
|
597
|
+
# @param unit [Integer] Time unit retrieve into the length parameter.
|
|
598
|
+
# @see TimeUnit
|
|
599
|
+
# @return [Integer] the length in the requested units.
|
|
600
|
+
def length(unit = TimeUnit::MS)
|
|
601
|
+
value = "\0" * SIZEOF_INT
|
|
602
|
+
FMOD.invoke(:Sound_GetLength, self, value, unit)
|
|
603
|
+
value.unpack1('L')
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
##
|
|
607
|
+
# Retrieves the loop points for a sound.
|
|
608
|
+
# @param start_unit [Integer] The time format used for the returned loop
|
|
609
|
+
# start point.
|
|
610
|
+
# @see TimeUnit
|
|
611
|
+
# @param end_unit [Integer] The time format used for the returned loop end
|
|
612
|
+
# point.
|
|
613
|
+
# @see TimeUnit
|
|
614
|
+
# @return [Array(Integer, Integer)] the loop points in an array where the
|
|
615
|
+
# first element is the start loop point, and second element is the end
|
|
616
|
+
# loop point in the requested time units.
|
|
617
|
+
def loop_points(start_unit = TimeUnit::MS, end_unit = TimeUnit::MS)
|
|
618
|
+
loop_start, loop_end = "\0" * SIZEOF_INT, "\0" * SIZEOF_INT
|
|
619
|
+
FMOD.invoke(:Sound_GetLoopPoints, self, loop_start,
|
|
620
|
+
start_unit, loop_end, end_unit)
|
|
621
|
+
[loop_start.unpack1('L'), loop_end.unpack1('L')]
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
##
|
|
625
|
+
# Sets the loop points within a sound
|
|
626
|
+
#
|
|
627
|
+
# If a sound was 44100 samples long and you wanted to loop the whole sound,
|
|
628
|
+
# _loop_start_ would be 0, and _loop_end_ would be 44099, not 44100. You
|
|
629
|
+
# wouldn't use milliseconds in this case because they are not sample
|
|
630
|
+
# accurate.
|
|
631
|
+
#
|
|
632
|
+
# If loop end is smaller or equal to loop start, it will result in an error.
|
|
633
|
+
#
|
|
634
|
+
# If loop start or loop end is larger than the length of the sound, it will
|
|
635
|
+
# result in an error
|
|
636
|
+
#
|
|
637
|
+
# @param loop_start [Integer] The loop start point. This point in time is
|
|
638
|
+
# played, so it is inclusive.
|
|
639
|
+
# @param loop_end [Integer] The loop end point. This point in time is
|
|
640
|
+
# played, so it is inclusive
|
|
641
|
+
# @param start_unit [Integer] The time format used for the loop start point.
|
|
642
|
+
# @see TimeUnit
|
|
643
|
+
# @param end_unit [Integer] The time format used for the loop end point.
|
|
644
|
+
# @see TimeUnit
|
|
645
|
+
#
|
|
646
|
+
# @return [void]
|
|
647
|
+
def set_loop(loop_start, loop_end, start_unit = TimeUnit::MS, end_unit = TimeUnit::MS)
|
|
648
|
+
FMOD.invoke(:Sound_SetLoopPoints, self, loop_start,
|
|
649
|
+
start_unit, loop_end, end_unit)
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
# @!group MOD/S3M/XM/IT/MIDI
|
|
653
|
+
|
|
654
|
+
##
|
|
655
|
+
# @!attribute music_speed
|
|
656
|
+
# The relative speed of MOD/S3M/XM/IT/MIDI music.
|
|
657
|
+
# * *Minimum:* 0.01
|
|
658
|
+
# * *Maximum:* 100.0
|
|
659
|
+
# * *Default:* 1.0
|
|
660
|
+
# 0.5 is half speed, 2.0 is double speed, etc.
|
|
661
|
+
# @return [Float] the relative speed of the song.
|
|
662
|
+
float_reader(:music_speed, :Sound_GetMusicSpeed)
|
|
663
|
+
float_writer(:music_speed=, :Sound_SetMusicSpeed)
|
|
664
|
+
|
|
665
|
+
##
|
|
666
|
+
# Retrieves the volume of a MOD/S3M/XM/IT/MIDI music channel volume.
|
|
667
|
+
# @param channel [Integer] MOD/S3M/XM/IT/MIDI music sub-channel to retrieve
|
|
668
|
+
# the volume for.
|
|
669
|
+
# @return [Float] the volume of the channel from 0.0 to 1.0.
|
|
670
|
+
# * *Default:* 1.0
|
|
671
|
+
# @see set_music_volume
|
|
672
|
+
def music_volume(channel)
|
|
673
|
+
volume = "\0" * SIZEOF_FLOAT
|
|
674
|
+
FMOD.invoke(:Sound_GetMusicChannelVolume, self, channel, volume)
|
|
675
|
+
volume.unpack1('f')
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
##
|
|
679
|
+
# Sets the volume of a MOD/S3M/XM/IT/MIDI music channel volume.
|
|
680
|
+
# @param channel [Integer] MOD/S3M/XM/IT/MIDI music sub-channel to set a
|
|
681
|
+
# linear volume for.
|
|
682
|
+
# @param volume [Float] Volume of the channel.
|
|
683
|
+
# * *Minimum:* 0.0
|
|
684
|
+
# * *Maximum:* 1.0
|
|
685
|
+
# * *Default:* 1.0
|
|
686
|
+
# @return [void]
|
|
687
|
+
def set_music_volume(channel, volume)
|
|
688
|
+
volume = volume.clamp(0.0, 1.0)
|
|
689
|
+
FMOD.invoke(:Sound_SetMusicChannelVolume, self, channel, volume)
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
##
|
|
693
|
+
# @!attribute [r] music_channels
|
|
694
|
+
# @return [Integer] the number of channels inside a MOD/S3M/XM/IT/MIDI file.
|
|
695
|
+
integer_reader(:music_channels, :Sound_GetMusicNumChannels)
|
|
696
|
+
|
|
697
|
+
# @!endgroup
|
|
698
|
+
|
|
699
|
+
##
|
|
700
|
+
# Retrieves the state a sound is in after {Mode::NON_BLOCKING} has been used
|
|
701
|
+
# to open it, or the state of the streaming buffer.
|
|
702
|
+
#
|
|
703
|
+
# @return [OpenState] the current state of the sound.
|
|
704
|
+
def open_state
|
|
705
|
+
args = ["\0" * SIZEOF_INT, "\0" * SIZEOF_INT, "\0" * SIZEOF_INT, "\0" * SIZEOF_INT]
|
|
706
|
+
FMOD.invoke(:Sound_GetOpenState, self, *args)
|
|
707
|
+
args = args.join.unpack('lLll')
|
|
708
|
+
OpenState.send(:new, *args)
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
##
|
|
712
|
+
# Retrieves information on an embedded sync point. These points can be user
|
|
713
|
+
# generated or can come from a wav file with embedded markers.
|
|
714
|
+
# @param syncpoint [Pointer] A handle to a sync-point.
|
|
715
|
+
# @param time_unit [Integer] A {TimeUnit} parameter to determine a desired
|
|
716
|
+
# format for the offset parameter.
|
|
717
|
+
# @return [Array(String, Integer)] array containing the name of the
|
|
718
|
+
# sync-point and the offset in the requested time unit.
|
|
719
|
+
# @see TimeUnit
|
|
720
|
+
def syncpoint_info(syncpoint, time_unit = TimeUnit::MS)
|
|
721
|
+
name, offset = "\0" * 256, "\0" * SIZEOF_INT
|
|
722
|
+
FMOD.invoke(:Sound_GetSyncPointInfo, self, syncpoint,
|
|
723
|
+
name, 256, offset, time_unit)
|
|
724
|
+
[name.delete("\0"), offset.unpack1('L')]
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
##
|
|
728
|
+
# Plays a sound object on a particular channel and {ChannelGroup}.
|
|
729
|
+
#
|
|
730
|
+
# When a sound is played, it will use the sound's default frequency and
|
|
731
|
+
# priority.
|
|
732
|
+
#
|
|
733
|
+
# A sound defined as {Mode::THREE_D} will by default play at the position of
|
|
734
|
+
# the listener.
|
|
735
|
+
#
|
|
736
|
+
# Channels are reference counted. If a channel is stolen by the FMOD
|
|
737
|
+
# priority system, then the handle to the stolen voice becomes invalid, and
|
|
738
|
+
# Channel based commands will not affect the new sound playing in its place.
|
|
739
|
+
# If all channels are currently full playing a sound, FMOD will steal a
|
|
740
|
+
# channel with the lowest priority sound. If more channels are playing than
|
|
741
|
+
# are currently available on the sound-card/sound device or software mixer,
|
|
742
|
+
# then FMOD will "virtualize" the channel. This type of channel is not
|
|
743
|
+
# heard, but it is updated as if it was playing. When its priority becomes
|
|
744
|
+
# high enough or another sound stops that was using a real hardware/software
|
|
745
|
+
# channel, it will start playing from where it should be. This technique
|
|
746
|
+
# saves CPU time (thousands of sounds can be played at once without actually
|
|
747
|
+
# being mixed or taking up resources), and also removes the need for the
|
|
748
|
+
# user to manage voices themselves. An example of virtual channel usage is a
|
|
749
|
+
# dungeon with 100 torches burning, all with a looping crackling sound, but
|
|
750
|
+
# with a sound-card that only supports 32 hardware voices. If the 3D
|
|
751
|
+
# positions and priorities for each torch are set correctly, FMOD will play
|
|
752
|
+
# all 100 sounds without any "out of channels" errors, and swap the real
|
|
753
|
+
# voices in and out according to which torches are closest in 3D space.
|
|
754
|
+
# Priority for virtual channels can be changed in the sound's defaults, or
|
|
755
|
+
# at runtime with {Channel.priority}.
|
|
756
|
+
#
|
|
757
|
+
# @param group [ChannelGroup] The {ChannelGroup} become a member of. This is
|
|
758
|
+
# more efficient than later setting with {Channel.group}, as it does it
|
|
759
|
+
# during the channel setup, rather than connecting to the master channel
|
|
760
|
+
# group, then later disconnecting and connecting to the new {ChannelGroup}
|
|
761
|
+
# when specified. Specify +nil+ to ignore (use master {ChannelGroup}).
|
|
762
|
+
# @return [Channel] the newly playing channel.
|
|
763
|
+
def play(group = nil)
|
|
764
|
+
parent.play_sound(self, group, false)
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
class TagCollection
|
|
769
|
+
|
|
770
|
+
include Enumerable
|
|
771
|
+
|
|
772
|
+
private_class_method :new
|
|
773
|
+
|
|
774
|
+
def initialize(sound)
|
|
775
|
+
@sound = sound
|
|
776
|
+
end
|
|
777
|
+
|
|
778
|
+
def each
|
|
779
|
+
return to_enum(:each) unless block_given?
|
|
780
|
+
(0...count).each { |i| yield self[i] }
|
|
781
|
+
self
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
def count
|
|
785
|
+
buffer = "\0" * Fiddle::SIZEOF_INT
|
|
786
|
+
FMOD.invoke(:Sound_GetNumTags, @sound, buffer, nil)
|
|
787
|
+
buffer.unpack1('l')
|
|
788
|
+
end
|
|
789
|
+
|
|
790
|
+
alias_method :size, :count
|
|
791
|
+
|
|
792
|
+
def updated_count
|
|
793
|
+
buffer = "\0" * Fiddle::SIZEOF_INT
|
|
794
|
+
FMOD.invoke(:Sound_GetNumTags, @sound, nil, buffer)
|
|
795
|
+
buffer.unpack1('l')
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
def [](index)
|
|
799
|
+
tag = FMOD::Core::Tag.new
|
|
800
|
+
if index.is_a?(Integer)
|
|
801
|
+
return nil unless index.between?(0, count - 1)
|
|
802
|
+
FMOD.invoke(:Sound_GetTag, @sound, nil, index, tag)
|
|
803
|
+
elsif tag.is_a?(String)
|
|
804
|
+
FMOD.invoke(:Sound_GetTag, @sound, index, 0, tag)
|
|
805
|
+
end
|
|
806
|
+
tag
|
|
807
|
+
end
|
|
808
|
+
end
|
|
809
|
+
end
|
|
810
|
+
end
|