fmod 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.travis.yml +5 -0
- data/.yardopts +2 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +96 -0
- data/Rakefile +1 -0
- data/bin/console +28 -0
- data/bin/setup +8 -0
- data/ext/fmod.dll +0 -0
- data/ext/fmod64.dll +0 -0
- data/ext/libfmod.dylib +0 -0
- data/ext/llbfmod.zip +0 -0
- data/extras/FMOD Studio Programmers API for Windows.chm +0 -0
- data/fmod.gemspec +58 -0
- data/lib/fmod.rb +564 -0
- data/lib/fmod/channel.rb +151 -0
- data/lib/fmod/channel_control.rb +821 -0
- data/lib/fmod/channel_group.rb +61 -0
- data/lib/fmod/core.rb +35 -0
- data/lib/fmod/core/bool_description.rb +18 -0
- data/lib/fmod/core/channel_mask.rb +24 -0
- data/lib/fmod/core/data_description.rb +14 -0
- data/lib/fmod/core/driver.rb +59 -0
- data/lib/fmod/core/dsp_description.rb +7 -0
- data/lib/fmod/core/dsp_index.rb +9 -0
- data/lib/fmod/core/dsp_type.rb +43 -0
- data/lib/fmod/core/extensions.rb +28 -0
- data/lib/fmod/core/file_system.rb +86 -0
- data/lib/fmod/core/filter_type.rb +19 -0
- data/lib/fmod/core/float_description.rb +16 -0
- data/lib/fmod/core/guid.rb +50 -0
- data/lib/fmod/core/init_flags.rb +19 -0
- data/lib/fmod/core/integer_description.rb +26 -0
- data/lib/fmod/core/mode.rb +36 -0
- data/lib/fmod/core/output_type.rb +30 -0
- data/lib/fmod/core/parameter_info.rb +41 -0
- data/lib/fmod/core/parameter_type.rb +10 -0
- data/lib/fmod/core/result.rb +88 -0
- data/lib/fmod/core/reverb.rb +217 -0
- data/lib/fmod/core/sound_ex_info.rb +7 -0
- data/lib/fmod/core/sound_format.rb +30 -0
- data/lib/fmod/core/sound_group_behavior.rb +9 -0
- data/lib/fmod/core/sound_type.rb +80 -0
- data/lib/fmod/core/speaker_index.rb +18 -0
- data/lib/fmod/core/speaker_mode.rb +16 -0
- data/lib/fmod/core/spectrum_data.rb +12 -0
- data/lib/fmod/core/structure.rb +23 -0
- data/lib/fmod/core/structures.rb +41 -0
- data/lib/fmod/core/tag.rb +51 -0
- data/lib/fmod/core/tag_data_type.rb +14 -0
- data/lib/fmod/core/time_unit.rb +40 -0
- data/lib/fmod/core/vector.rb +42 -0
- data/lib/fmod/core/window_type.rb +12 -0
- data/lib/fmod/dsp.rb +510 -0
- data/lib/fmod/dsp_connection.rb +113 -0
- data/lib/fmod/effects.rb +38 -0
- data/lib/fmod/effects/channel_mix.rb +101 -0
- data/lib/fmod/effects/chorus.rb +30 -0
- data/lib/fmod/effects/compressor.rb +52 -0
- data/lib/fmod/effects/convolution_reverb.rb +31 -0
- data/lib/fmod/effects/delay.rb +44 -0
- data/lib/fmod/effects/distortion.rb +16 -0
- data/lib/fmod/effects/dsps.rb +10 -0
- data/lib/fmod/effects/echo.rb +37 -0
- data/lib/fmod/effects/envelope_follower.rb +31 -0
- data/lib/fmod/effects/fader.rb +16 -0
- data/lib/fmod/effects/fft.rb +38 -0
- data/lib/fmod/effects/flange.rb +37 -0
- data/lib/fmod/effects/high_pass.rb +24 -0
- data/lib/fmod/effects/high_pass_simple.rb +25 -0
- data/lib/fmod/effects/it_echo.rb +56 -0
- data/lib/fmod/effects/it_lowpass.rb +36 -0
- data/lib/fmod/effects/ladspa_plugin.rb +14 -0
- data/lib/fmod/effects/limiter.rb +32 -0
- data/lib/fmod/effects/loudness_meter.rb +19 -0
- data/lib/fmod/effects/low_pass.rb +25 -0
- data/lib/fmod/effects/low_pass_simple.rb +26 -0
- data/lib/fmod/effects/mixer.rb +11 -0
- data/lib/fmod/effects/multiband_eq.rb +153 -0
- data/lib/fmod/effects/normalize.rb +47 -0
- data/lib/fmod/effects/object_pan.rb +62 -0
- data/lib/fmod/effects/oscillator.rb +52 -0
- data/lib/fmod/effects/pan.rb +166 -0
- data/lib/fmod/effects/param_eq.rb +36 -0
- data/lib/fmod/effects/pitch_shift.rb +47 -0
- data/lib/fmod/effects/return.rb +18 -0
- data/lib/fmod/effects/send.rb +21 -0
- data/lib/fmod/effects/sfx_reverb.rb +87 -0
- data/lib/fmod/effects/three_eq.rb +41 -0
- data/lib/fmod/effects/transceiver.rb +57 -0
- data/lib/fmod/effects/tremolo.rb +67 -0
- data/lib/fmod/effects/vst_plugin.rb +12 -0
- data/lib/fmod/effects/winamp_plugin.rb +12 -0
- data/lib/fmod/error.rb +108 -0
- data/lib/fmod/geometry.rb +380 -0
- data/lib/fmod/handle.rb +129 -0
- data/lib/fmod/reverb3D.rb +98 -0
- data/lib/fmod/sound.rb +810 -0
- data/lib/fmod/sound_group.rb +54 -0
- data/lib/fmod/system.rb +1242 -0
- data/lib/fmod/version.rb +3 -0
- metadata +220 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
module FMOD
|
2
|
+
module Core
|
3
|
+
##
|
4
|
+
# These definitions describe the native format of the hardware or software
|
5
|
+
# buffer that will be used.
|
6
|
+
module SoundFormat
|
7
|
+
##
|
8
|
+
# Uninitialized / unknown.
|
9
|
+
NONE = 0
|
10
|
+
##
|
11
|
+
# 8-bit integer PCM data.
|
12
|
+
PCM_8 = 1
|
13
|
+
##
|
14
|
+
# 16-bit integer PCM data.
|
15
|
+
PCM_16 = 2
|
16
|
+
##
|
17
|
+
# 24-bit integer PCM data.
|
18
|
+
PCM_24 = 3
|
19
|
+
##
|
20
|
+
# 32-bit integer PCM data.
|
21
|
+
PCM_32 = 4
|
22
|
+
##
|
23
|
+
# 32-bit floating point PCM data.
|
24
|
+
PCM_FLOAT = 5
|
25
|
+
##
|
26
|
+
# Sound data is in its native compressed format.
|
27
|
+
BIT_STREAM = 6
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module FMOD
|
2
|
+
module Core
|
3
|
+
##
|
4
|
+
# These definitions describe the type of song being played.
|
5
|
+
module SoundType
|
6
|
+
##
|
7
|
+
# 3rd party / unknown plugin format.
|
8
|
+
UNKNOWN = 0
|
9
|
+
##
|
10
|
+
# AIFF.
|
11
|
+
AIFF = 1
|
12
|
+
##
|
13
|
+
# Microsoft Advanced Systems Format (ie WMA/ASF/WMV).
|
14
|
+
ASF = 2
|
15
|
+
##
|
16
|
+
# Sound font / downloadable sound bank.
|
17
|
+
DLS= 3
|
18
|
+
##
|
19
|
+
# FLAC loss-less codec.
|
20
|
+
FLAC = 4
|
21
|
+
##
|
22
|
+
# FMOD Sample Bank.
|
23
|
+
FSB = 5
|
24
|
+
##
|
25
|
+
# Impulse Tracker.
|
26
|
+
IT = 6
|
27
|
+
##
|
28
|
+
# MIDI.
|
29
|
+
MIDI = 7
|
30
|
+
##
|
31
|
+
# Pro-tracker / Fast-tracker MOD.
|
32
|
+
MOD = 8
|
33
|
+
##
|
34
|
+
# MP2/MP3 MPEG.
|
35
|
+
MPEG = 9
|
36
|
+
##
|
37
|
+
# Ogg vorbis.
|
38
|
+
OGG_VORBIS = 10
|
39
|
+
##
|
40
|
+
# Information only from ASX/PLS/M3U/WAX play-lists.
|
41
|
+
PLAY_LIST = 11
|
42
|
+
##
|
43
|
+
# Raw PCM data.
|
44
|
+
RAW = 12
|
45
|
+
##
|
46
|
+
# ScreamTracker 3.
|
47
|
+
S3M = 13
|
48
|
+
##
|
49
|
+
# User created sound.
|
50
|
+
USER = 14
|
51
|
+
##
|
52
|
+
# Microsoft WAV.
|
53
|
+
WAV = 15
|
54
|
+
##
|
55
|
+
# FastTracker 2 XM.
|
56
|
+
XM = 16
|
57
|
+
##
|
58
|
+
# Xbox360 XMA.
|
59
|
+
XMA = 17
|
60
|
+
##
|
61
|
+
# iPhone hardware decoder, supports AAC, ALAC and MP3.
|
62
|
+
AUDIO_QUEUE = 18
|
63
|
+
##
|
64
|
+
# PS4 / PSVita ATRAC 9 format.
|
65
|
+
AT9 = 19
|
66
|
+
##
|
67
|
+
# Vorbis.
|
68
|
+
VORBIS = 20
|
69
|
+
##
|
70
|
+
# Windows Store Application built in system codecs.
|
71
|
+
MEDIA_FOUNDATION = 21
|
72
|
+
##
|
73
|
+
# Android MediaCodec.
|
74
|
+
MEDIA_CODEC = 22
|
75
|
+
##
|
76
|
+
# FMOD Adaptive Differential Pulse Code Modulation.
|
77
|
+
FAD_PCM = 23
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module FMOD
|
2
|
+
module Core
|
3
|
+
module SpeakerIndex
|
4
|
+
FRONT_LEFT = 0
|
5
|
+
FRONT_RIGHT = 1
|
6
|
+
FRONT_CENTER = 2
|
7
|
+
LOW_FREQUENCY = 3
|
8
|
+
SURROUND_LEFT = 4
|
9
|
+
SURROUND_RIGHT = 5
|
10
|
+
BACK_LEFT = 6
|
11
|
+
BACK_RIGHT = 7
|
12
|
+
TOP_FRONT_LEFT = 8
|
13
|
+
TOP_FRONT_RIGHT = 9
|
14
|
+
TOP_BACK_LEFT = 10
|
15
|
+
TOP_BACK_RIGHT = 11
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'fiddle'
|
2
|
+
|
3
|
+
module FMOD
|
4
|
+
module Core
|
5
|
+
class Structure < Fiddle::CStructEntity
|
6
|
+
|
7
|
+
include Fiddle
|
8
|
+
include FMOD::Core
|
9
|
+
|
10
|
+
def initialize(address, types, members)
|
11
|
+
address = Pointer[address] if address.is_a?(String)
|
12
|
+
address ||= Fiddle.malloc(self.class.size(types)).to_i
|
13
|
+
super(address, types)
|
14
|
+
assign_names members
|
15
|
+
end
|
16
|
+
|
17
|
+
def inspect
|
18
|
+
values = @members.map { |sym| "#{sym}=#{self[sym]}"}.join(', ')
|
19
|
+
super.sub(/free=0x(.)*/, values << '>')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
module FMOD
|
3
|
+
|
4
|
+
module Core
|
5
|
+
|
6
|
+
WetDryMix = Struct.new(:pre_wet, :post_wet, :dry)
|
7
|
+
|
8
|
+
LoopPoints = Struct.new(:start, :start_unit, :end, :end_unit)
|
9
|
+
|
10
|
+
FadePoint = Struct.new(:clock, :volume)
|
11
|
+
|
12
|
+
##
|
13
|
+
# Defines the sound projection cone including the volume when outside the
|
14
|
+
# cone.
|
15
|
+
#
|
16
|
+
# @attr inside_angle [Float] Inside cone angle, in degrees. This is the
|
17
|
+
# angle within which the sound is at its normal volume.
|
18
|
+
#
|
19
|
+
# Must not be greater than {#outside_angle}.
|
20
|
+
# * *Default:* 360.0
|
21
|
+
# @attr outside_angle [Float] Outside cone angle, in degrees. This is the
|
22
|
+
# angle outside of which the sound is at its outside volume.
|
23
|
+
#
|
24
|
+
# Must not be less than {#inside_angle}.
|
25
|
+
# * *Default:* 360.0
|
26
|
+
# @attr outside_volume [Float] Cone outside volume.
|
27
|
+
# * *Minimum:* 0.0
|
28
|
+
# * *Maximum:* 1.0
|
29
|
+
# * *Default:* 1.0
|
30
|
+
ConeSettings = Struct.new(:inside_angle, :outside_angle, :outside_volume)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module FMOD
|
2
|
+
module Core
|
3
|
+
class Tag < Structure
|
4
|
+
|
5
|
+
include Fiddle
|
6
|
+
|
7
|
+
def initialize(address = nil)
|
8
|
+
types = [TYPE_INT, TYPE_INT, TYPE_VOIDP, TYPE_VOIDP, TYPE_INT, TYPE_INT]
|
9
|
+
members = [:type, :data_type, :name, :data, :data_length, :updated]
|
10
|
+
super(address, types, members)
|
11
|
+
end
|
12
|
+
|
13
|
+
[:type, :data_type, :data_length].each do |symbol|
|
14
|
+
define_method(symbol) { self[symbol] }
|
15
|
+
end
|
16
|
+
|
17
|
+
def name
|
18
|
+
self[:name].to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def updated
|
22
|
+
self[:updated] != 0
|
23
|
+
end
|
24
|
+
|
25
|
+
def value
|
26
|
+
raw = self[:data].to_s(self[:data_length])
|
27
|
+
raw.delete!("\0") unless self[:data_type] == TagDataType::BINARY
|
28
|
+
# noinspection RubyResolve
|
29
|
+
case self[:data_type]
|
30
|
+
when TagDataType::BINARY then raw
|
31
|
+
when TagDataType::INT then raw.unpack1('l')
|
32
|
+
when TagDataType::FLOAT then raw.unpack1('f')
|
33
|
+
when TagDataType::STRING then raw.force_encoding('ASCII')
|
34
|
+
when TagDataType::STRING_UTF8 then raw.force_encoding(Encoding::UTF_8)
|
35
|
+
when TagDataType::STRING_UTF16 then raw.force_encoding(Encoding::UTF_16)
|
36
|
+
when TagDataType::STRING_UTF16BE then raw.force_encoding(Encoding::UTF_16BE)
|
37
|
+
else ''
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
begin
|
43
|
+
"#{value}"
|
44
|
+
rescue Encoding::CompatibilityError
|
45
|
+
value.inspect
|
46
|
+
# TODO
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module FMOD
|
2
|
+
module Core
|
3
|
+
module TimeUnit
|
4
|
+
|
5
|
+
##
|
6
|
+
# Milliseconds.
|
7
|
+
MS = 0x00000001
|
8
|
+
|
9
|
+
##
|
10
|
+
# PCM samples, related to milliseconds * sample rate / 1000.
|
11
|
+
PCM = 0x00000002
|
12
|
+
|
13
|
+
##
|
14
|
+
# Bytes, related to PCM samples * channels * data width (ie 16bit = 2 bytes).
|
15
|
+
PCM_BYTES = 0x00000004
|
16
|
+
|
17
|
+
##
|
18
|
+
# Raw file bytes of (compressed) sound data (does not include headers).
|
19
|
+
RAW_BYTES = 0x00000008
|
20
|
+
|
21
|
+
##
|
22
|
+
# Fractions of 1 PCM sample. Unsigned integer range 0 to 0xFFFFFFFF.
|
23
|
+
#
|
24
|
+
# Used for sub-sample granularity for DSP purposes.
|
25
|
+
PCM_FRACTION = 0x00000010
|
26
|
+
|
27
|
+
##
|
28
|
+
# MOD/S3M/XM/IT. Order in a sequenced module format.
|
29
|
+
MOD_ORDER = 0x00000100
|
30
|
+
|
31
|
+
##
|
32
|
+
# MOD/S3M/XM/IT. Current row in a sequenced module format.
|
33
|
+
MOD_ROW = 0x00000200
|
34
|
+
|
35
|
+
##
|
36
|
+
# MOD/S3M/XM/IT. Current pattern in a sequenced module format.
|
37
|
+
MOD_PATTERN = 0x00000400
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module FMOD
|
2
|
+
module Core
|
3
|
+
# @attr x [Float]
|
4
|
+
# @attr y [Float]
|
5
|
+
# @attr z [Float]
|
6
|
+
class Vector < Structure
|
7
|
+
|
8
|
+
def self.zero
|
9
|
+
new(0.0, 0.0, 0.0)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.one
|
13
|
+
new(1.0, 1.0, 1.0)
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(*args)
|
17
|
+
address ||= args.size == 1 ? args.first : nil
|
18
|
+
members = [:x, :y, :z]
|
19
|
+
types = Array.new(3, TYPE_FLOAT)
|
20
|
+
super(address, types, members)
|
21
|
+
set(*args) if args.size == 3
|
22
|
+
end
|
23
|
+
|
24
|
+
[:x, :y, :z].each do |symbol|
|
25
|
+
define_method(symbol) { self[symbol] }
|
26
|
+
define_method("#{symbol}=") { |value| self[symbol] = value.to_f }
|
27
|
+
end
|
28
|
+
|
29
|
+
def set(x, y, z)
|
30
|
+
self[:x], self[:y], self[:z] = x, y, z
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_a
|
34
|
+
@members.map { |sym| self[sym] }
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_h
|
38
|
+
{ x: self[:x], y: self[:y], z: self[:z] }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/fmod/dsp.rb
ADDED
@@ -0,0 +1,510 @@
|
|
1
|
+
module FMOD
|
2
|
+
|
3
|
+
# @attr wet_dry_mix [WetDryMix] Allows the user to scale the affect of a DSP
|
4
|
+
# effect, through control of the "wet" mix, which is the post-processed
|
5
|
+
# signal and the "dry" which is the pre-processed signal.
|
6
|
+
#
|
7
|
+
# The dry signal path is silent by default, because DSP effects transform
|
8
|
+
# the input and pass the newly processed result to the output. It does not
|
9
|
+
# add to the input.
|
10
|
+
class Dsp < Handle
|
11
|
+
|
12
|
+
ChannelFormat = Struct.new(:mask, :count, :speaker_mode)
|
13
|
+
|
14
|
+
DspInfo = Struct.new(:name, :version, :channels, :width, :height) do
|
15
|
+
def config?
|
16
|
+
width > 0 && height > 0
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Retrieves a hash mapping the class of each DSP to its corresponding
|
22
|
+
# integer type defined in {DspType}.
|
23
|
+
# @return [Hash{Integer=>Class}]
|
24
|
+
def self.type_map(type)
|
25
|
+
@type_map ||= {
|
26
|
+
DspType::MIXER => FMOD::Effects::Mixer,
|
27
|
+
DspType::OSCILLATOR => FMOD::Effects::Oscillator,
|
28
|
+
DspType::LOW_PASS => FMOD::Effects::LowPass,
|
29
|
+
DspType::IT_LOW_PASS => FMOD::Effects::ITLowPass,
|
30
|
+
DspType::HIGH_PASS => FMOD::Effects::HighPass,
|
31
|
+
DspType::ECHO => FMOD::Effects::Echo,
|
32
|
+
DspType::FADER => FMOD::Effects::Fader,
|
33
|
+
DspType::FLANGE => FMOD::Effects::Flange,
|
34
|
+
DspType::DISTORTION => FMOD::Effects::Distortion,
|
35
|
+
DspType::NORMALIZE => FMOD::Effects::Normalize,
|
36
|
+
DspType::LIMITER => FMOD::Effects::Limiter,
|
37
|
+
DspType::PARAM_EQ => FMOD::Effects::ParamEq,
|
38
|
+
DspType::PITCH_SHIFT => FMOD::Effects::PitchShift,
|
39
|
+
DspType::CHORUS => FMOD::Effects::Chorus,
|
40
|
+
DspType::VST_PLUGIN => FMOD::Effects::VstPlugin,
|
41
|
+
DspType::WINAMP_PLUGIN => FMOD::Effects::WinampPlugin,
|
42
|
+
DspType::IT_ECHO => FMOD::Effects::ITEcho,
|
43
|
+
DspType::COMPRESSOR => FMOD::Effects::Compressor,
|
44
|
+
DspType::SFX_REVERB => FMOD::Effects::SfxReverb,
|
45
|
+
DspType::LOW_PASS_SIMPLE => FMOD::Effects::LowPassSimple,
|
46
|
+
DspType::DELAY => FMOD::Effects::Delay,
|
47
|
+
DspType::TREMOLO => FMOD::Effects::Tremolo,
|
48
|
+
DspType::LADSPA_PLUGIN => FMOD::Effects::LadspaPlugin,
|
49
|
+
DspType::SEND => FMOD::Effects::Send,
|
50
|
+
DspType::RETURN => FMOD::Effects::Return,
|
51
|
+
DspType::HIGH_PASS_SIMPLE => FMOD::Effects::HighPassSimple,
|
52
|
+
DspType::PAN => FMOD::Effects::Pan,
|
53
|
+
DspType::THREE_EQ => FMOD::Effects::ThreeEq,
|
54
|
+
DspType::FFT => FMOD::Effects::FFT,
|
55
|
+
DspType::LOUDNESS_METER => FMOD::Effects::LoudnessMeter,
|
56
|
+
DspType::ENVELOPE_FOLLOWER => FMOD::Effects::EnvelopeFollower,
|
57
|
+
DspType::CONVOLUTION_REVERB => FMOD::Effects::ConvolutionReverb,
|
58
|
+
DspType::CHANNEL_MIX => FMOD::Effects::ChannelMix,
|
59
|
+
DspType::TRANSCEIVER => FMOD::Effects::Transceiver,
|
60
|
+
DspType::OBJECT_PAN => FMOD::Effects::ObjectPan,
|
61
|
+
DspType::MULTIBAND_EQ => FMOD::Effects::MultibandEq
|
62
|
+
}
|
63
|
+
return @type_map[type] if type.is_a?(Integer)
|
64
|
+
return @type_map.key(type) if type.is_a?(Class)
|
65
|
+
raise TypeError, "#{type} is not a Integer or Class."
|
66
|
+
end
|
67
|
+
|
68
|
+
def play(group = nil)
|
69
|
+
parent.play_dsp(self, group, false)
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Converts the specified memory address to a DSP of the appropriate
|
74
|
+
# sub-type instead of generic {Dsp} type.
|
75
|
+
# @param address [Integer|String|Pointer] The memory address of the DSP.
|
76
|
+
# @return [Dsp] A DSP class found in {FMOD::Effects}.
|
77
|
+
def self.from_handle(address)
|
78
|
+
address = address.unpack1('J') if address.is_a?(String)
|
79
|
+
type = "\0" * SIZEOF_INT
|
80
|
+
FMOD.invoke(:DSP_GetType, address.to_i, type)
|
81
|
+
klass = type_map(type.unpack1('l')) rescue Dsp
|
82
|
+
klass.new(address)
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# @!attribute [r] type
|
87
|
+
# Retrieves the pre-defined type of a FMOD registered DSP unit.
|
88
|
+
#
|
89
|
+
# @return [Integer]
|
90
|
+
# @see DspType
|
91
|
+
integer_reader(:type, :DSP_GetType)
|
92
|
+
|
93
|
+
##
|
94
|
+
# @!attribute [r] parameter_count
|
95
|
+
# Retrieves the number of parameters a DSP unit has to control its
|
96
|
+
# behavior.
|
97
|
+
#
|
98
|
+
# @return [Integer]
|
99
|
+
integer_reader(:parameter_count, :DSP_GetNumParameters)
|
100
|
+
|
101
|
+
##
|
102
|
+
# @!attribute [r] input_count
|
103
|
+
# Retrieves the number of inputs connected to the DSP unit.
|
104
|
+
#
|
105
|
+
# Inputs are units that feed data to this unit. When there are multiple
|
106
|
+
# inputs, they are mixed together.
|
107
|
+
#
|
108
|
+
# @note Because this function needs to flush the DSP queue before it can
|
109
|
+
# determine how many units are available, this function may block
|
110
|
+
# significantly while the background mixer thread operates.
|
111
|
+
# @return [Integer]
|
112
|
+
integer_reader(:input_count, :DSP_GetNumInputs)
|
113
|
+
|
114
|
+
##
|
115
|
+
# @!attribute [r] output_count
|
116
|
+
# Retrieves the number of outputs connected to the DSP unit.
|
117
|
+
#
|
118
|
+
# Outputs are units that this unit feeds data to. When there are multiple
|
119
|
+
# outputs, the data is split and sent to each unit individually.
|
120
|
+
#
|
121
|
+
# @note Because this function needs to flush the DSP queue before it can
|
122
|
+
# determine how many units are available, this function may block
|
123
|
+
# significantly while the background mixer thread operates.
|
124
|
+
#
|
125
|
+
# @return [Integer]
|
126
|
+
integer_reader(:output_count, :DSP_GetNumOutputs)
|
127
|
+
|
128
|
+
##
|
129
|
+
# @!method idle?
|
130
|
+
# Retrieves the idle state of a DSP. A DSP is idle when no signal is
|
131
|
+
# coming into it. This can be a useful method of determining if a DSP sub
|
132
|
+
# branch is finished processing, so it can be disconnected for example.
|
133
|
+
#
|
134
|
+
# @return [Boolean]
|
135
|
+
bool_reader(:idle?, :DSP_GetIdle)
|
136
|
+
|
137
|
+
##
|
138
|
+
# @!attribute active
|
139
|
+
# Gets or sets the enabled state of a unit for being processed.
|
140
|
+
#
|
141
|
+
# This does not connect or disconnect a unit in any way, it just disables
|
142
|
+
# it so that it is not processed.
|
143
|
+
#
|
144
|
+
# If a unit is disabled, and has inputs, they will also cease to be
|
145
|
+
# processed.
|
146
|
+
#
|
147
|
+
# To disable a unit but allow the inputs of the unit to continue being
|
148
|
+
# processed, use {#bypass} instead.
|
149
|
+
#
|
150
|
+
# @return [Boolean]
|
151
|
+
bool_reader(:active, :DSP_GetActive)
|
152
|
+
bool_writer(:active=, :DSP_SetActive)
|
153
|
+
|
154
|
+
##
|
155
|
+
# @!attribute bypass
|
156
|
+
# Enables or disables the read callback of a DSP unit so that it does or
|
157
|
+
# doesn't process the data coming into it.
|
158
|
+
#
|
159
|
+
# A DSP unit that is disabled still processes its inputs, it will just be
|
160
|
+
# "dry".
|
161
|
+
#
|
162
|
+
# If a unit is bypassed, it will still process its inputs.
|
163
|
+
#
|
164
|
+
# To disable the unit and all of its inputs, use {#active} instead.
|
165
|
+
# @return [Boolean]
|
166
|
+
bool_reader(:bypass, :DSP_GetBypass)
|
167
|
+
bool_writer(:bypass=, :DSP_SetBypass)
|
168
|
+
|
169
|
+
##
|
170
|
+
# Retrieves the name of this DSP unit.
|
171
|
+
# @return [String]
|
172
|
+
def name
|
173
|
+
name = "\0" * 32
|
174
|
+
FMOD.invoke(:DSP_GetInfo, self, name, nil, nil, nil, nil)
|
175
|
+
name.delete("\0")
|
176
|
+
end
|
177
|
+
|
178
|
+
##
|
179
|
+
# Retrieves the version of this DSP as string.
|
180
|
+
# @return [String]
|
181
|
+
def version
|
182
|
+
vs = "\0" * SIZEOF_INT
|
183
|
+
FMOD.invoke(:DSP_GetInfo, self, nil, vs, nil, nil, nil)
|
184
|
+
version = vs.unpack1('L').to_s(16).rjust(8, '0')
|
185
|
+
"#{version[0, 4].to_i}.#{version[4, 4].to_i}"
|
186
|
+
end
|
187
|
+
|
188
|
+
def wet_dry_mix
|
189
|
+
args = ["\0" * SIZEOF_FLOAT, "\0" * SIZEOF_FLOAT, "\0" * SIZEOF_FLOAT]
|
190
|
+
FMOD.invoke(:DSP_GetWetDryMix, self, *args)
|
191
|
+
args.map! { |arg| arg.unpack1('f') }
|
192
|
+
WetDryMix.new(*args)
|
193
|
+
end
|
194
|
+
|
195
|
+
def wet_dry_mix=(mix)
|
196
|
+
FMOD.type?(mix, WetDryMix)
|
197
|
+
FMOD.invoke(:DSP_SetWetDryMix, self, *mix.values)
|
198
|
+
mix
|
199
|
+
end
|
200
|
+
|
201
|
+
##
|
202
|
+
# Allows the user to scale the affect of a DSP effect, through control of
|
203
|
+
# the "wet" mix, which is the post-processed signal and the "dry" which is
|
204
|
+
# the pre-processed signal.
|
205
|
+
#
|
206
|
+
# The dry signal path is silent by default, because dsp effects transform
|
207
|
+
# the input and pass the newly processed result to the output. It does not
|
208
|
+
# add to the input.
|
209
|
+
#
|
210
|
+
# @param pre_wet [Float] Floating point value from 0 to 1, describing a
|
211
|
+
# linear scale of the "wet" (pre-processed signal) mix of the effect.
|
212
|
+
# Scale can be lower than 0 (negating) and higher than 1 (amplifying). *
|
213
|
+
# * *Default:* 1.0
|
214
|
+
# @param post_wet [Float] Floating point value from 0 to 1, describing a
|
215
|
+
# linear scale of the "wet" (post-processed signal) mix of the effect.
|
216
|
+
# Scale can be lower than 0 (negating) and higher than 1 (amplifying).
|
217
|
+
# * *Default:* 1.0
|
218
|
+
# @param dry [Float] Floating point value from 0 to 1, describing a linear
|
219
|
+
# scale of the "dry" (pre-processed signal) mix of the effect. Scale can
|
220
|
+
# be lower than 0 and higher than 1 (amplifying).
|
221
|
+
# * *Default:* 0.0
|
222
|
+
#
|
223
|
+
# @return [self]
|
224
|
+
def set_wet_dry_mix(pre_wet, post_wet, dry)
|
225
|
+
FMOD.invoke(:DSP_SetWetDryMix, self, pre_wet, post_wet, dry)
|
226
|
+
self
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
|
231
|
+
|
232
|
+
|
233
|
+
|
234
|
+
|
235
|
+
|
236
|
+
|
237
|
+
def channel_format
|
238
|
+
args = ["\0" * SIZEOF_INT, "\0" * SIZEOF_INT, "\0" * SIZEOF_INT]
|
239
|
+
FMOD.invoke(:DSP_GetChannelFormat, self, *args)
|
240
|
+
args.map! { |arg| arg.unpack1('L') }
|
241
|
+
ChannelFormat.new(*args)
|
242
|
+
end
|
243
|
+
|
244
|
+
def channel_format=(format)
|
245
|
+
FMOD.type?(format, ChannelFormat)
|
246
|
+
FMOD.invoke(:DSP_GetChannelFormat, self, *format.values)
|
247
|
+
format
|
248
|
+
end
|
249
|
+
|
250
|
+
def param_info(index)
|
251
|
+
return nil unless FMOD.valid_range?(index, 0, parameter_count, false)
|
252
|
+
FMOD.invoke(:DSP_GetParameterInfo, self, index, info = int_ptr)
|
253
|
+
ParameterInfo.new(info.unpack1('J'))
|
254
|
+
end
|
255
|
+
|
256
|
+
def to_s
|
257
|
+
"#{name} v.#{version}"
|
258
|
+
end
|
259
|
+
|
260
|
+
def info
|
261
|
+
name, vs = "\0" * 32, "\0" * SIZEOF_INT
|
262
|
+
args = ["\0" * SIZEOF_INT, "\0" * SIZEOF_INT, "\0" * SIZEOF_INT]
|
263
|
+
FMOD.invoke(:DSP_GetInfo, self, name, vs, *args)
|
264
|
+
args = args.map { |arg| arg.unpack1('l') }
|
265
|
+
vs = vs.unpack1('L').to_s(16).rjust(8, '0')
|
266
|
+
version = "#{vs[0, 4].to_i}.#{vs[4, 4].to_i}"
|
267
|
+
DspInfo.new(name.delete("\0"), version, *args)
|
268
|
+
end
|
269
|
+
|
270
|
+
def reset
|
271
|
+
FMOD.invoke(:DSP_Reset, self)
|
272
|
+
end
|
273
|
+
|
274
|
+
def parent
|
275
|
+
FMOD.invoke(:DSP_GetSystemObject, self, system = int_ptr)
|
276
|
+
System.new(system)
|
277
|
+
end
|
278
|
+
|
279
|
+
def show_dialog(hwnd, show = true)
|
280
|
+
FMOD.invoke(:DSP_ShowConfigDialog, self, hwnd, show.to_i)
|
281
|
+
end
|
282
|
+
|
283
|
+
def disconnect(inputs, outputs)
|
284
|
+
FMOD.invoke(:DSP_DisconnectAll, self, inputs.to_i, outputs.to_i)
|
285
|
+
end
|
286
|
+
|
287
|
+
def disconnect_from(dsp, connection = nil)
|
288
|
+
FMOD.type?(dsp, Dsp) unless dsp.nil?
|
289
|
+
FMOD.type?(connection, DspConnection) unless connection.nil?
|
290
|
+
FMOD.invoke(:DSP_DisconnectFrom, self, dsp, connection)
|
291
|
+
end
|
292
|
+
|
293
|
+
def output_format(*args)
|
294
|
+
if args.size == 3 && args.all? { |arg| arg.is_a?(Integer) }
|
295
|
+
mask, count, mode = args
|
296
|
+
elsif args.size == 1 && FMOD.type?(args[0], ChannelFormat)
|
297
|
+
mask, count, mode = args[0].values
|
298
|
+
else
|
299
|
+
mask, count, mode = channel_format.values
|
300
|
+
end
|
301
|
+
args = ["\0" * SIZEOF_INT, "\0" * SIZEOF_INT, "\0" * SIZEOF_INT]
|
302
|
+
FMOD.invoke(:DSP_GetOutputChannelFormat, mask, count, mode, *args)
|
303
|
+
args.map! { |arg| arg.unpack1('L') }
|
304
|
+
ChannelFormat.new(*args)
|
305
|
+
end
|
306
|
+
|
307
|
+
def input(index)
|
308
|
+
input = int_ptr
|
309
|
+
FMOD.invoke(:DSP_GetInput, self, index, input, nil)
|
310
|
+
Dsp.from_handle(input)
|
311
|
+
end
|
312
|
+
|
313
|
+
def output(index)
|
314
|
+
output = int_ptr
|
315
|
+
FMOD.invoke(:DSP_GetOutput, self, index, output, nil)
|
316
|
+
Dsp.from_handle(output)
|
317
|
+
end
|
318
|
+
|
319
|
+
def input_connection(index)
|
320
|
+
connection = int_ptr
|
321
|
+
FMOD.invoke(:DSP_GetInput, self, index, nil, connection)
|
322
|
+
DspConnection.new(connection)
|
323
|
+
end
|
324
|
+
|
325
|
+
def output_connection(index)
|
326
|
+
connection = int_ptr
|
327
|
+
FMOD.invoke(:DSP_GetOutput, self, index, nil, connection)
|
328
|
+
DspConnection.new(connection)
|
329
|
+
end
|
330
|
+
|
331
|
+
def add_input(dsp, type)
|
332
|
+
FMOD.type?(dsp, Dsp)
|
333
|
+
connection = int_ptr
|
334
|
+
FMOD.invoke(:DSP_AddInput, self, dsp, connection, type)
|
335
|
+
DspConnection.new(connection)
|
336
|
+
end
|
337
|
+
|
338
|
+
def input_metering?
|
339
|
+
enabled = "\0" * SIZEOF_INT
|
340
|
+
FMOD.invoke(:DSP_GetMeteringEnabled, self, enabled, nil)
|
341
|
+
enabled.unpack1('l') != 0
|
342
|
+
end
|
343
|
+
|
344
|
+
def output_metering?
|
345
|
+
enabled = "\0" * SIZEOF_INT
|
346
|
+
FMOD.invoke(:DSP_GetMeteringEnabled, self, nil, enabled)
|
347
|
+
enabled.unpack1('l') != 0
|
348
|
+
end
|
349
|
+
|
350
|
+
def enable_metering(input, output)
|
351
|
+
FMOD.invoke(:DSP_SetMeteringEnabled, self, input ? 1 :0, output.to_i)
|
352
|
+
end
|
353
|
+
|
354
|
+
def [](index)
|
355
|
+
return nil unless FMOD.valid_range?(index, 0, parameter_count, false)
|
356
|
+
case param_info(index).type
|
357
|
+
when ParameterType::FLOAT then get_float(index)
|
358
|
+
when ParameterType::INT then get_integer(index)
|
359
|
+
when ParameterType::BOOL then get_integer(index)
|
360
|
+
when ParameterType::DATA then get_data(index)
|
361
|
+
else raise RangeError, 'Unknown data type.'
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
def get_float(index)
|
366
|
+
return nil unless FMOD.valid_range?(index, 0, parameter_count, false)
|
367
|
+
buffer = "\0" * SIZEOF_FLOAT
|
368
|
+
FMOD.invoke(:DSP_GetParameterFloat, self, index, buffer, nil, 0)
|
369
|
+
buffer.unpack1('f')
|
370
|
+
end
|
371
|
+
|
372
|
+
def get_integer(index)
|
373
|
+
return nil unless FMOD.valid_range?(index, 0, parameter_count, false)
|
374
|
+
buffer = "\0" * SIZEOF_INT
|
375
|
+
FMOD.invoke(:DSP_GetParameterInt, self, index, buffer, nil, 0)
|
376
|
+
buffer.unpack1('l')
|
377
|
+
end
|
378
|
+
|
379
|
+
def get_bool(index)
|
380
|
+
return nil unless FMOD.valid_range?(index, 0, parameter_count, false)
|
381
|
+
buffer = "\0" * SIZEOF_INT
|
382
|
+
FMOD.invoke(:DSP_GetParameterBool, self, index, buffer, nil, 0)
|
383
|
+
buffer.unpack1('l') != 0
|
384
|
+
end
|
385
|
+
|
386
|
+
def get_data(index)
|
387
|
+
return nil unless FMOD.valid_range?(index, 0, parameter_count, false)
|
388
|
+
pointer = int_ptr
|
389
|
+
size = "\0" * SIZEOF_INT
|
390
|
+
FMOD.invoke(:DSP_GetParameterData, self, index, pointer, size, nil, 0)
|
391
|
+
Pointer.new(pointer.unpack('J'), size.unpack1('l'))
|
392
|
+
end
|
393
|
+
|
394
|
+
def []=(index, value)
|
395
|
+
return unless FMOD.valid_range?(index, 0, parameter_count, false)
|
396
|
+
case value
|
397
|
+
when Float then set_float(index, value)
|
398
|
+
when Integer then set_integer(index, value)
|
399
|
+
when TrueClass, FalseClass, NilClass then set_bool(index, value)
|
400
|
+
when String, Pointer then set_data(index, value)
|
401
|
+
else raise TypeError, "#{value} is not a valid DSP parameter type."
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
def set_float(index, value)
|
406
|
+
return unless FMOD.valid_range?(index, 0, parameter_count, false)
|
407
|
+
FMOD.invoke(:DSP_SetParameterFloat, self, index, value)
|
408
|
+
self
|
409
|
+
end
|
410
|
+
|
411
|
+
def set_integer(index, value)
|
412
|
+
return unless FMOD.valid_range?(index, 0, parameter_count, false)
|
413
|
+
FMOD.invoke(:DSP_SetParameterInt, self, index, value)
|
414
|
+
self
|
415
|
+
end
|
416
|
+
|
417
|
+
def set_bool(index, value)
|
418
|
+
return unless FMOD.valid_range?(index, 0, parameter_count, false)
|
419
|
+
FMOD.invoke(:DSP_SetParameterBool, self, index, value.to_i)
|
420
|
+
self
|
421
|
+
end
|
422
|
+
|
423
|
+
def set_data(index, value)
|
424
|
+
return unless FMOD.valid_range?(index, 0, parameter_count, false)
|
425
|
+
unless FMOD.type?(value, String, false)
|
426
|
+
FMOD.type?(value, Pointer)
|
427
|
+
end
|
428
|
+
size = value.is_a?(String) ? value.bytesize : value.size
|
429
|
+
FMOD.invoke(:DSP_SetParameterData, self, index, value, size)
|
430
|
+
self
|
431
|
+
end
|
432
|
+
|
433
|
+
|
434
|
+
class << self
|
435
|
+
|
436
|
+
private
|
437
|
+
|
438
|
+
def float_param(index, name, **options)
|
439
|
+
define_method(name) { get_float(index) } unless options[:write_only]
|
440
|
+
unless options[:readonly]
|
441
|
+
define_method("#{name}=") do |value|
|
442
|
+
value = options[:min] if options[:min] && value < options[:min]
|
443
|
+
value = options[:max] if options[:max] && value > options[:max]
|
444
|
+
set_float(index, value)
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
def integer_param(index, name, **options)
|
450
|
+
define_method(name) { get_integer(index) } unless options[:write_only]
|
451
|
+
unless options[:readonly]
|
452
|
+
define_method("#{name}=") do |value|
|
453
|
+
value = options[:min] if options[:min] && value < options[:min]
|
454
|
+
value = options[:max] if options[:max] && value > options[:max]
|
455
|
+
set_float(index, value)
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
def bool_param(index, name, **options)
|
461
|
+
define_method(name) { get_bool(index) } unless options[:write_only]
|
462
|
+
unless options[:readonly]
|
463
|
+
define_method("#{name}=") { |value| set_bool(index, value) }
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
def data_param(index, name, **options)
|
468
|
+
define_method(name) { get_data(index) } unless options[:write_only]
|
469
|
+
unless options[:readonly]
|
470
|
+
define_method("#{name}=") { |value| set_data(index, value) }
|
471
|
+
end
|
472
|
+
end
|
473
|
+
end
|
474
|
+
# def self.float_param(index, name, **options)
|
475
|
+
# define_method(name) { get_float(index) } unless options[:write_only]
|
476
|
+
# unless options[:readonly]
|
477
|
+
# define_method("#{name}=") do |value|
|
478
|
+
# value = options[:min] if options[:min] && value < options[:min]
|
479
|
+
# value = options[:max] if options[:max] && value > options[:max]
|
480
|
+
# set_float(index, value)
|
481
|
+
# end
|
482
|
+
# end
|
483
|
+
# end
|
484
|
+
#
|
485
|
+
# def self.integer_param(index, name, **options)
|
486
|
+
# define_method(name) { get_integer(index) } unless options[:write_only]
|
487
|
+
# unless options[:readonly]
|
488
|
+
# define_method("#{name}=") do |value|
|
489
|
+
# value = options[:min] if options[:min] && value < options[:min]
|
490
|
+
# value = options[:max] if options[:max] && value > options[:max]
|
491
|
+
# set_float(index, value)
|
492
|
+
# end
|
493
|
+
# end
|
494
|
+
# end
|
495
|
+
#
|
496
|
+
# def self.bool_param(index, name, **options)
|
497
|
+
# define_method(name) { get_bool(index) } unless options[:write_only]
|
498
|
+
# unless options[:readonly]
|
499
|
+
# define_method("#{name}=") { |value| set_bool(index, value) }
|
500
|
+
# end
|
501
|
+
# end
|
502
|
+
#
|
503
|
+
# def self.data_param(index, name, **options)
|
504
|
+
# define_method(name) { get_data(index) } unless options[:write_only]
|
505
|
+
# unless options[:readonly]
|
506
|
+
# define_method("#{name}=") { |value| set_data(index, value) }
|
507
|
+
# end
|
508
|
+
# end
|
509
|
+
end
|
510
|
+
end
|