fmod-ruby 0.0.1 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +15 -15
- data/Rakefile +10 -0
- data/examples/ComputerMagic.mp3 +0 -0
- data/examples/ComputerMagic.wav +0 -0
- data/examples/Flute3.wav +0 -0
- data/examples/concat.rb +41 -0
- data/examples/output/concat.wav +0 -0
- data/examples/output/write.wav +0 -0
- data/examples/play.rb +19 -0
- data/examples/play2.rb +27 -0
- data/{scratch.rb → examples/scratch.rb} +11 -3
- data/examples/volume.rb +25 -0
- data/examples/write.rb +36 -0
- data/fmod-ruby.gemspec +1 -0
- data/lib/fmod.rb +6 -4
- data/lib/fmod/channel.rb +45 -13
- data/lib/fmod/convert.rb +19 -0
- data/lib/fmod/enums.rb +72 -3
- data/lib/fmod/functions.rb +26 -1
- data/lib/fmod/sound.rb +27 -17
- data/lib/fmod/system.rb +67 -27
- data/lib/fmod/version.rb +1 -1
- metadata +28 -7
- data/lib/fmod/pointer_helper.rb +0 -8
data/README.md
CHANGED
@@ -1,22 +1,9 @@
|
|
1
|
-
<!-- Efficient, realtime audio for Ruby
|
2
|
-
==================================
|
3
|
-
|
4
|
-
Ruby is too slow to do realtime audio, but C is not. The best way to give Ruby robust audio capabilities is to use a native C/C++ library. The primary problem then is which library to choose and how to wire it into Ruby.
|
5
|
-
|
6
|
-
So how do you choose an audio library? Aside from writing one from scratch, there are a number of widely used preexisting C/C++ audio libraries. At the lowest level, there are projects like PortAudio which handle direct interfacing with the hardware only, requiring a programmer to feed it a stream of numbers representing a waveform. At the highest level, projects like CLAM, CSL, FMOD and others provide a full framework complete with voice management, sound file playback, and even synthesis. In these frameworks, the low level number pushing is well abstracted, leaving the programmer with a coarse interface to predefined functionality.
|
7
|
-
|
8
|
-
This Ruby library has chosen a high level framework as a basis for realtime audio. While it certainly is possible to get Ruby to push waveform representations to a low level API like PortAudio, doing so 44100 times a second is not playing to Ruby's strengths. By using a high level library, we trade flexible synthesis for a limited set of fast, efficient audio routines.
|
9
|
-
|
10
|
-
To get Ruby to use external native libraries, there are a few ways to go about it. You can write a C external, use the DL library available in the Ruby standard distribution, or you can use FFI. Having tried all three methods of interfacing with C, FFI is a clear winner. It is efficient, predictable, effortlessly cross platform, and decently well documented.
|
11
|
-
|
12
|
-
Ruby has been sorely lacking an audio framework, and an FMOD wrapper is a great place to start. This library is ultimately meant to be the basis of audio DSLs written in Ruby. -->
|
13
|
-
|
14
1
|
FMOD for Ruby
|
15
2
|
-------------
|
16
3
|
|
17
4
|
*ruby-fmod* wraps the excellent [FMOD](http://www.fmod.org) C/C++ libraries using the equally excellent [FFI](http://github.com/ffi/ffi).
|
18
5
|
|
19
|
-
It is far from
|
6
|
+
It is far from complete, but all the basic techniques for interfacing Ruby with the FMOD library are outlined. The authors have (so far) fleshed out only the parts needed for their own projects. Please do fork this project and contribute to it.
|
20
7
|
|
21
8
|
FMOD?
|
22
9
|
=====
|
@@ -28,7 +15,7 @@ FMOD is not free, nor is it open source, but it does have a very [liberal licens
|
|
28
15
|
Installation
|
29
16
|
============
|
30
17
|
|
31
|
-
To use this Ruby library, you must have the FMOD on you machine. [Go get it](http://www.fmod.org/index.php/download).
|
18
|
+
To use this Ruby library, you must have the FMOD on you machine. [Go get it](http://www.fmod.org/index.php/download).
|
32
19
|
|
33
20
|
That being said:
|
34
21
|
|
@@ -38,7 +25,18 @@ or, in a Gemfile
|
|
38
25
|
|
39
26
|
gem "fmod-ruby", :require => "fmod"
|
40
27
|
|
28
|
+
Testing
|
29
|
+
=======
|
30
|
+
|
31
|
+
No formal test suite yet. However, the +examples/+ directory includes several examples that should all work.
|
32
|
+
|
33
|
+
History
|
34
|
+
=======
|
41
35
|
|
36
|
+
* release 0.1.1 - added Channel#set_volume, Sound#release
|
37
|
+
* release 0.1.0 - added keyword options to System#initialize and whatnot to support offline wav creation (examples write.rb and contact.rb)
|
38
|
+
|
39
|
+
* release 0.0.1 - initial gemification
|
42
40
|
|
43
41
|
Usage
|
44
42
|
=====
|
@@ -49,3 +47,5 @@ Usage
|
|
49
47
|
sound = FMOD::Sound.new(file)
|
50
48
|
sound.play
|
51
49
|
|
50
|
+
See +examples/*+ for more examples
|
51
|
+
|
data/Rakefile
CHANGED
@@ -1,2 +1,12 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
2
|
require "bundler/gem_tasks"
|
3
|
+
require 'rdoc/task'
|
4
|
+
Rake::RDocTask.new do |rdoc|
|
5
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
6
|
+
|
7
|
+
rdoc.main = 'README.rdoc'
|
8
|
+
rdoc.rdoc_dir = 'rdoc'
|
9
|
+
rdoc.title = "array_floe #{version}"
|
10
|
+
rdoc.rdoc_files.include('README*')
|
11
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
12
|
+
end
|
Binary file
|
Binary file
|
data/examples/Flute3.wav
ADDED
Binary file
|
data/examples/concat.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
Bundler.require
|
6
|
+
require 'fmod'
|
7
|
+
|
8
|
+
DIR = File.dirname(__FILE__)
|
9
|
+
PROG = File.basename(__FILE__, '.*')
|
10
|
+
|
11
|
+
|
12
|
+
infile1 = (ARGV.empty?) ? "#{DIR}/ComputerMagic.mp3" : ARGV.shift
|
13
|
+
infile2 = (ARGV.empty?) ? "#{DIR}/Flute3.wav" : ARGV.shift
|
14
|
+
outfile = "#{DIR}/output/#{PROG}.wav"
|
15
|
+
puts "writing to #{outfile}"
|
16
|
+
|
17
|
+
puts "calling FMOD.init"
|
18
|
+
FMOD.init(:output_wav => outfile)
|
19
|
+
|
20
|
+
sound1 = FMOD::Sound.new(infile1)
|
21
|
+
puts "#{infile1} length=#{sound1.length}ms"
|
22
|
+
sound2 = FMOD::Sound.new(infile2)
|
23
|
+
puts "#{infile2} length=#{sound2.length}ms"
|
24
|
+
|
25
|
+
puts "scheduleing play..."
|
26
|
+
sound1.play
|
27
|
+
sound2.play(:paused => true)
|
28
|
+
sound2.channel.set_delay(FMOD::Convert.ms_to_samples(sound1.length), :delaytype => :FMOD_DELAYTYPE_DSPCLOCK_START)
|
29
|
+
sound2.channel.set_paused(false)
|
30
|
+
|
31
|
+
samples = FMOD::Convert.ms_to_samples(sound1.length + sound2.length)
|
32
|
+
puts "updating to #{samples} samples"
|
33
|
+
|
34
|
+
while (dsp_clock = FMOD.system.dsp_clock) < samples
|
35
|
+
pct = (100.0*dsp_clock/samples).round
|
36
|
+
puts " dsp_clock = #{dsp_clock} (#{pct}% of #{samples})" if pct % 10 == 0
|
37
|
+
FMOD.system.update
|
38
|
+
end
|
39
|
+
|
40
|
+
puts "done. releasing."
|
41
|
+
FMOD.system.release
|
Binary file
|
Binary file
|
data/examples/play.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
Bundler.require
|
6
|
+
require 'fmod'
|
7
|
+
DIR = File.dirname(__FILE__)
|
8
|
+
|
9
|
+
puts "calling FMOD.init..."
|
10
|
+
FMOD.init
|
11
|
+
|
12
|
+
file = (ARGV.empty?) ? "#{DIR}/ComputerMagic.mp3" : ARGV[0]
|
13
|
+
|
14
|
+
puts "creating sound #{file}..."
|
15
|
+
sound = FMOD::Sound.new(file)
|
16
|
+
puts "#{file} length=#{sound.length}ms"
|
17
|
+
sound.play
|
18
|
+
puts "scheduled play. now starting to sleep"
|
19
|
+
sleep (sound.length/1000.0).ceil
|
data/examples/play2.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
Bundler.require
|
6
|
+
require 'fmod'
|
7
|
+
|
8
|
+
DIR = File.dirname(__FILE__)
|
9
|
+
PROG = File.basename(__FILE__, '.*')
|
10
|
+
|
11
|
+
|
12
|
+
infile1 = (ARGV.empty?) ? "#{DIR}/ComputerMagic.mp3" : ARGV.shift
|
13
|
+
infile2 = (ARGV.empty?) ? "#{DIR}/Flute3.wav" : ARGV.shift
|
14
|
+
|
15
|
+
puts "calling FMOD.init"
|
16
|
+
FMOD.init
|
17
|
+
|
18
|
+
sound1 = FMOD::Sound.new(infile1)
|
19
|
+
puts "#{infile1} length=#{sound1.length}ms"
|
20
|
+
sound2 = FMOD::Sound.new(infile2)
|
21
|
+
puts "#{infile2} length=#{sound2.length}ms"
|
22
|
+
|
23
|
+
puts "scheduling play..."
|
24
|
+
sound1.play
|
25
|
+
sound2.play
|
26
|
+
puts "scheduled play. now starting to sleep"
|
27
|
+
sleep ([sound1.length,sound2.length].max/1000.0).ceil
|
@@ -1,4 +1,9 @@
|
|
1
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
Bundler.require
|
6
|
+
require 'fmod'
|
2
7
|
|
3
8
|
start_time = Time.now
|
4
9
|
puts "initializing FMOD ..."
|
@@ -6,7 +11,9 @@ FMOD.init
|
|
6
11
|
end_time = Time.new - start_time
|
7
12
|
puts "done in #{end_time} seconds."
|
8
13
|
|
9
|
-
|
14
|
+
DIR = File.dirname(__FILE__)
|
15
|
+
|
16
|
+
file = (ARGV.empty?) ? "#{DIR}/ComputerMagic.mp3" : ARGV[0]
|
10
17
|
|
11
18
|
|
12
19
|
puts "initializing a sound ..."
|
@@ -17,6 +24,7 @@ puts "done in #{end_time} seconds."
|
|
17
24
|
sound.play
|
18
25
|
puts FMOD.system.version
|
19
26
|
puts sound.channel.frequency
|
27
|
+
puts "sound length=#{sound.length}ms"
|
20
28
|
sleep 2
|
21
29
|
# sound.channel.frequency = 11050.0
|
22
30
|
# puts sound.channel.frequency
|
@@ -46,4 +54,4 @@ end
|
|
46
54
|
# block
|
47
55
|
while true
|
48
56
|
sleep 5
|
49
|
-
end
|
57
|
+
end
|
data/examples/volume.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
Bundler.require
|
6
|
+
require 'fmod'
|
7
|
+
DIR = File.dirname(__FILE__)
|
8
|
+
|
9
|
+
puts "calling FMOD.init..."
|
10
|
+
FMOD.init
|
11
|
+
|
12
|
+
file = (ARGV.empty?) ? "#{DIR}/Flute3.wav" : ARGV[0]
|
13
|
+
|
14
|
+
puts "creating sound #{file}..."
|
15
|
+
sound = FMOD::Sound.new(file)
|
16
|
+
puts "#{file} length=#{sound.length}ms"
|
17
|
+
puts "playing at full volume:"
|
18
|
+
sound.play
|
19
|
+
sleep (sound.length/1000.0).ceil
|
20
|
+
|
21
|
+
puts "playing at quarter volume:"
|
22
|
+
sound.play(:paused => true)
|
23
|
+
sound.channel.set_volume(0.25)
|
24
|
+
sound.channel.set_paused(false)
|
25
|
+
sleep (sound.length/1000.0).ceil
|
data/examples/write.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
Bundler.require
|
6
|
+
require 'fmod'
|
7
|
+
|
8
|
+
DIR = File.dirname(__FILE__)
|
9
|
+
PROG = File.basename(__FILE__, '.*')
|
10
|
+
|
11
|
+
|
12
|
+
infile = (ARGV.empty?) ? "#{DIR}/ComputerMagic.mp3" : ARGV[0]
|
13
|
+
outfile = "#{DIR}/output/#{PROG}.wav"
|
14
|
+
puts "writing to #{outfile}"
|
15
|
+
|
16
|
+
puts "calling FMOD.init"
|
17
|
+
FMOD.init(:output_wav => outfile)
|
18
|
+
|
19
|
+
puts "creating sound #{infile}"
|
20
|
+
sound = FMOD::Sound.new(infile)
|
21
|
+
puts "#{infile} length=#{sound.length}ms"
|
22
|
+
|
23
|
+
puts "scheduling play..."
|
24
|
+
sound.play
|
25
|
+
|
26
|
+
samples = FMOD::Convert.ms_to_samples(sound.length)
|
27
|
+
puts "updating until #{samples} samples"
|
28
|
+
|
29
|
+
while (dsp_clock = FMOD.system.dsp_clock) < samples
|
30
|
+
pct = (100.0*dsp_clock/samples).round
|
31
|
+
puts " dsp_clock = #{dsp_clock} (#{pct}% of #{samples})" if pct % 10 == 0
|
32
|
+
FMOD.system.update
|
33
|
+
end
|
34
|
+
|
35
|
+
puts "done. releasing."
|
36
|
+
FMOD.system.release
|
data/fmod-ruby.gemspec
CHANGED
data/lib/fmod.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'ffi'
|
2
|
+
require 'hash_keyword_args'
|
2
3
|
|
3
4
|
require 'fmod/version'
|
4
5
|
|
@@ -6,13 +7,14 @@ require 'fmod/constants'
|
|
6
7
|
require 'fmod/enums'
|
7
8
|
require 'fmod/functions'
|
8
9
|
|
9
|
-
require 'fmod/
|
10
|
-
require 'fmod/sound'
|
10
|
+
require 'fmod/convert'
|
11
11
|
require 'fmod/channel'
|
12
|
+
require 'fmod/sound'
|
13
|
+
require 'fmod/system'
|
12
14
|
|
13
15
|
module FMOD
|
14
|
-
def self.init
|
15
|
-
@system = System.new
|
16
|
+
def self.init(*args)
|
17
|
+
@system = System.new(*args)
|
16
18
|
end
|
17
19
|
|
18
20
|
# Ruby version of the system
|
data/lib/fmod/channel.rb
CHANGED
@@ -6,50 +6,82 @@ module FMOD
|
|
6
6
|
|
7
7
|
# TODO: Need to find a way to reliably destroy this pointer
|
8
8
|
def initialize(options = {})
|
9
|
-
@
|
10
|
-
@pointer = FFI::MemoryPointer.new(:pointer)
|
9
|
+
@memory = FFI::MemoryPointer.new(:pointer)
|
11
10
|
end
|
11
|
+
|
12
|
+
def pointer
|
13
|
+
@memory.read_pointer
|
14
|
+
end
|
15
|
+
|
16
|
+
def pointer_addr
|
17
|
+
@memory
|
18
|
+
end
|
19
|
+
|
12
20
|
|
13
21
|
# result = FMOD_Channel_IsPlaying(channel, &playing);
|
14
22
|
def playing?
|
15
23
|
FFI::MemoryPointer.new(:int) do |ptr|
|
16
|
-
error_check FMOD_Channel_IsPlaying(
|
17
|
-
return
|
24
|
+
error_check FMOD_Channel_IsPlaying(pointer, ptr)
|
25
|
+
return ptr.read_int == 1
|
18
26
|
end
|
19
27
|
end
|
20
28
|
|
21
29
|
def pause
|
22
|
-
error_check FMOD_Channel_SetPaused(
|
23
|
-
|
30
|
+
error_check FMOD_Channel_SetPaused(pointer, ((paused?) ? 0 : 1))
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_paused(bool)
|
34
|
+
error_check FMOD_Channel_SetPaused(pointer, (bool ? 1 : 0))
|
24
35
|
end
|
25
36
|
|
26
37
|
def paused?
|
27
38
|
FFI::MemoryPointer.new(:int) do |ptr|
|
28
|
-
error_check FMOD_Channel_GetPaused(
|
39
|
+
error_check FMOD_Channel_GetPaused(pointer, ptr)
|
29
40
|
return ptr.read_int == 1
|
30
41
|
end
|
31
42
|
end
|
32
43
|
|
33
44
|
def set_position(int = 0)
|
34
|
-
error_check FMOD_Channel_SetPosition(
|
45
|
+
error_check FMOD_Channel_SetPosition(pointer, int, FMOD_TIMEUNIT_MS)
|
35
46
|
end
|
36
47
|
|
37
48
|
def pan=(float)
|
38
49
|
float = 1.0 if float > 1.0
|
39
50
|
float = 0.0 if float < 0.0
|
40
|
-
error_check FMOD_Channel_SetPan(
|
51
|
+
error_check FMOD_Channel_SetPan(pointer, float)
|
41
52
|
end
|
42
53
|
|
43
54
|
def frequency
|
44
55
|
FFI::MemoryPointer.new(:float) do |ptr|
|
45
|
-
error_check FMOD_Channel_GetFrequency(
|
56
|
+
error_check FMOD_Channel_GetFrequency(pointer, ptr)
|
46
57
|
return ptr.read_float
|
47
58
|
end
|
48
59
|
end
|
49
60
|
|
50
61
|
def frequency=(float)
|
51
|
-
error_check FMOD_Channel_SetFrequency(
|
62
|
+
error_check FMOD_Channel_SetFrequency(pointer, float)
|
52
63
|
end
|
53
|
-
|
64
|
+
|
65
|
+
def set_delay(samples, opts={})
|
66
|
+
opts = opts.keyword_args(:delaytype => :required)
|
67
|
+
hi, lo = Convert.int64_to_32(samples)
|
68
|
+
error_check FMOD_Channel_SetDelay(pointer, opts.delaytype, hi, lo)
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_delay(opts={})
|
72
|
+
opts = opts.keyword_args(:delaytype => :required)
|
73
|
+
|
74
|
+
FFI::MemoryPointer.new(:int) { |hi|
|
75
|
+
FFI::MemoryPointer.new(:int) { |lo|
|
76
|
+
error_check FMOD_Channel_GetDelay(pointer, opts.delaytype, hi, lo)
|
77
|
+
return Convert.int32_to_64(hi.read_int, lo.read_int)
|
78
|
+
}
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def set_volume(volume)
|
83
|
+
error_check FMOD_Channel_SetVolume(pointer, volume)
|
84
|
+
end
|
85
|
+
|
54
86
|
end
|
55
|
-
end
|
87
|
+
end
|
data/lib/fmod/convert.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module FMOD
|
2
|
+
module Convert
|
3
|
+
|
4
|
+
# since ruby will automatically convert to a Bignum if needed, this
|
5
|
+
# should work fine even on 32-bit platforms
|
6
|
+
def self.int32_to_64(hi32, lo32)
|
7
|
+
(hi32 << 32) | lo32
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.int64_to_32(int64)
|
11
|
+
[(int64 & 0xFFFFFFFF00000000) >> 32, int64 & 0xFFFFFFFF]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.ms_to_samples(ms, opts={})
|
15
|
+
opts = opts.keyword_args(:sample_rate => FMOD.system.sample_rate) # samples per second
|
16
|
+
ms * opts.sample_rate / 1000
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/fmod/enums.rb
CHANGED
@@ -3,8 +3,8 @@ module FMOD
|
|
3
3
|
extend FFI::Library
|
4
4
|
|
5
5
|
FMOD_CHANNELINDEX = enum(
|
6
|
-
:FMOD_CHANNEL_FREE,
|
7
|
-
:FMOD_CHANNEL_REUSE
|
6
|
+
:FMOD_CHANNEL_FREE, -1,
|
7
|
+
:FMOD_CHANNEL_REUSE, -2
|
8
8
|
)
|
9
9
|
|
10
10
|
FMOD_RESULT = enum(
|
@@ -132,5 +132,74 @@ module FMOD
|
|
132
132
|
:FMOD_DSP_TYPE_DELAY,
|
133
133
|
:FMOD_DSP_TYPE_TREMOLO
|
134
134
|
)
|
135
|
+
|
136
|
+
FMOD_OUTPUTTYPE = enum(
|
137
|
+
:FMOD_OUTPUTTYPE_AUTODETECT,
|
138
|
+
:FMOD_OUTPUTTYPE_UNKNOWN,
|
139
|
+
:FMOD_OUTPUTTYPE_NOSOUND,
|
140
|
+
:FMOD_OUTPUTTYPE_WAVWRITER,
|
141
|
+
:FMOD_OUTPUTTYPE_NOSOUND_NRT,
|
142
|
+
:FMOD_OUTPUTTYPE_WAVWRITER_NRT,
|
143
|
+
:FMOD_OUTPUTTYPE_DSOUND,
|
144
|
+
:FMOD_OUTPUTTYPE_WINMM,
|
145
|
+
:FMOD_OUTPUTTYPE_WASAPI,
|
146
|
+
:FMOD_OUTPUTTYPE_ASIO,
|
147
|
+
:FMOD_OUTPUTTYPE_OSS,
|
148
|
+
:FMOD_OUTPUTTYPE_ALSA,
|
149
|
+
:FMOD_OUTPUTTYPE_ESD,
|
150
|
+
:FMOD_OUTPUTTYPE_PULSEAUDIO,
|
151
|
+
:FMOD_OUTPUTTYPE_COREAUDIO,
|
152
|
+
:FMOD_OUTPUTTYPE_XBOX360,
|
153
|
+
:FMOD_OUTPUTTYPE_PSP,
|
154
|
+
:FMOD_OUTPUTTYPE_PS3,
|
155
|
+
:FMOD_OUTPUTTYPE_NGP,
|
156
|
+
:FMOD_OUTPUTTYPE_WII,
|
157
|
+
:FMOD_OUTPUTTYPE_3DS,
|
158
|
+
:FMOD_OUTPUTTYPE_AUDIOTRACK,
|
159
|
+
:FMOD_OUTPUTTYPE_OPENSL,
|
160
|
+
:FMOD_OUTPUTTYPE_NACL,
|
161
|
+
:FMOD_OUTPUTTYPE_MAX,
|
162
|
+
:FMOD_OUTPUTTYPE_FORCEINT, 65536
|
163
|
+
)
|
164
|
+
|
165
|
+
FMOD_SOUND_FORMAT = enum(
|
166
|
+
:FMOD_SOUND_FORMAT_NONE,
|
167
|
+
:FMOD_SOUND_FORMAT_PCM8,
|
168
|
+
:FMOD_SOUND_FORMAT_PCM16,
|
169
|
+
:FMOD_SOUND_FORMAT_PCM24,
|
170
|
+
:FMOD_SOUND_FORMAT_PCM32,
|
171
|
+
:FMOD_SOUND_FORMAT_PCMFLOAT,
|
172
|
+
:FMOD_SOUND_FORMAT_GCADPCM,
|
173
|
+
:FMOD_SOUND_FORMAT_IMAADPCM,
|
174
|
+
:FMOD_SOUND_FORMAT_VAG,
|
175
|
+
:FMOD_SOUND_FORMAT_HEVAG,
|
176
|
+
:FMOD_SOUND_FORMAT_XMA,
|
177
|
+
:FMOD_SOUND_FORMAT_MPEG,
|
178
|
+
:FMOD_SOUND_FORMAT_CELT,
|
179
|
+
:FMOD_SOUND_FORMAT_AT9,
|
180
|
+
:FMOD_SOUND_FORMAT_XWMA,
|
181
|
+
:FMOD_SOUND_FORMAT_MAX,
|
182
|
+
:FMOD_SOUND_FORMAT_FORCEINT, 65536
|
183
|
+
)
|
184
|
+
|
185
|
+
FMOD_DSP_RESAMPLER = enum(
|
186
|
+
:FMOD_DSP_RESAMPLER_NOINTERP,
|
187
|
+
:FMOD_DSP_RESAMPLER_LINEAR,
|
188
|
+
:FMOD_DSP_RESAMPLER_CUBIC,
|
189
|
+
:FMOD_DSP_RESAMPLER_SPLINE,
|
190
|
+
:FMOD_DSP_RESAMPLER_MAX,
|
191
|
+
:FMOD_DSP_RESAMPLER_FORCEINT, 65536
|
192
|
+
)
|
193
|
+
|
194
|
+
FMOD_DELAYTYPE = enum(
|
195
|
+
:FMOD_DELAYTYPE_END_MS,
|
196
|
+
:FMOD_DELAYTYPE_DSPCLOCK_START,
|
197
|
+
:FMOD_DELAYTYPE_DSPCLOCK_END,
|
198
|
+
:FMOD_DELAYTYPE_DSPCLOCK_PAUSE,
|
199
|
+
|
200
|
+
:FMOD_DELAYTYPE_MAX,
|
201
|
+
:FMOD_DELAYTYPE_FORCEINT, 65536
|
202
|
+
)
|
203
|
+
|
135
204
|
end
|
136
|
-
end
|
205
|
+
end
|
data/lib/fmod/functions.rb
CHANGED
@@ -31,10 +31,15 @@ module FMOD
|
|
31
31
|
attach_function :FMOD_System_Create, [:pointer], FMOD_RESULT
|
32
32
|
attach_function :FMOD_System_GetVersion, [:pointer, :pointer], FMOD_RESULT
|
33
33
|
attach_function :FMOD_System_Init, [:pointer, :int, :int, :pointer], FMOD_RESULT
|
34
|
+
attach_function :FMOD_System_Update, [:pointer], FMOD_RESULT
|
35
|
+
attach_function :FMOD_System_Release, [:pointer], FMOD_RESULT
|
36
|
+
attach_function :FMOD_System_GetDSPClock, [:pointer, :pointer, :pointer], FMOD_RESULT
|
37
|
+
attach_function :FMOD_System_GetDSPBufferSize, [:pointer, :pointer, :pointer], FMOD_RESULT
|
38
|
+
attach_function :FMOD_System_SetSoftwareFormat, [:pointer, :int, FMOD_SOUND_FORMAT, :int, :int, FMOD_DSP_RESAMPLER], FMOD_RESULT
|
34
39
|
|
35
40
|
attach_function :FMOD_System_CreateSound, [:pointer, :string, :int, :int, :pointer], FMOD_RESULT
|
36
41
|
attach_function :FMOD_System_CreateStream, [:pointer, :string, :int, :int, :pointer], FMOD_RESULT
|
37
|
-
attach_function :FMOD_System_PlaySound, [:pointer,
|
42
|
+
attach_function :FMOD_System_PlaySound, [:pointer, FMOD_CHANNELINDEX, :pointer, :int, :pointer], FMOD_RESULT
|
38
43
|
|
39
44
|
# FMOD_RESULT FMOD_System_GetDriverInfo(FMOD_SYSTEM * system, int id, char * name, int namelen, FMOD_GUID * guid);
|
40
45
|
# attach_function :FMOD_System_GetDriverInfo
|
@@ -48,6 +53,13 @@ module FMOD
|
|
48
53
|
# FMOD_RESULT FMOD_System_GetSoftwareChannels(FMOD_SYSTEM * system, int * numsoftwarechannels);
|
49
54
|
attach_function :FMOD_System_GetSoftwareChannels, [:pointer, :pointer], FMOD_RESULT
|
50
55
|
|
56
|
+
# FMOD_RESULT F_API FMOD_System_SetOutput (FMOD_SYSTEM *system, FMOD_OUTPUTTYPE output);
|
57
|
+
attach_function :FMOD_System_SetOutput, [:pointer, FMOD_OUTPUTTYPE], FMOD_RESULT
|
58
|
+
|
59
|
+
# FMOD_RESULT F_API FMOD_System_GetOutput (FMOD_SYSTEM *system, FMOD_OUTPUTTYPE *output);
|
60
|
+
attach_function :FMOD_System_GetOutput, [:pointer, :pointer], FMOD_RESULT
|
61
|
+
|
62
|
+
|
51
63
|
# FMOD_RESULT FMOD_Channel_IsPlaying(FMOD_CHANNEL * channel, FMOD_BOOL * isplaying);
|
52
64
|
attach_function :FMOD_Channel_IsPlaying, [:pointer, :pointer], FMOD_RESULT
|
53
65
|
|
@@ -69,6 +81,19 @@ module FMOD
|
|
69
81
|
# FMOD_RESULT FMOD_Channel_SetFrequency(FMOD_CHANNEL * channel, float frequency);
|
70
82
|
attach_function :FMOD_Channel_SetFrequency, [:pointer, :float], FMOD_RESULT
|
71
83
|
|
84
|
+
# FMOD_RESULT FMOD_Channel_SetDelay(FMOD_CHANNEL *channel, FMOD_DELAYTYPE delaytype, unsigned int delayhi, unsigned int delaylo);
|
85
|
+
attach_function :FMOD_Channel_SetDelay, [:pointer, FMOD_DELAYTYPE, :int, :int], FMOD_RESULT
|
86
|
+
|
87
|
+
# FMOD_RESULT FMOD_Channel_GetDelay(FMOD_CHANNEL *channel, FMOD_DELAYTYPE delaytype, unsigned int *delayhi, unsigned int *delaylo);
|
88
|
+
attach_function :FMOD_Channel_GetDelay, [:pointer, FMOD_DELAYTYPE, :pointer, :pointer], FMOD_RESULT
|
89
|
+
|
90
|
+
attach_function :FMOD_Channel_SetVolume, [:pointer, :float], FMOD_RESULT
|
91
|
+
|
92
|
+
|
93
|
+
# FMOD_RESULT F_API FMOD_Sound_GetLength (FMOD_SOUND *sound, unsigned int *length, FMOD_TIMEUNIT lengthtype);
|
94
|
+
attach_function :FMOD_Sound_GetLength, [:pointer, :pointer, :FMOD_TIMEUNIT], FMOD_RESULT
|
95
|
+
attach_function :FMOD_Sound_Release, [:pointer], FMOD_RESULT
|
96
|
+
|
72
97
|
|
73
98
|
def error_check(result)
|
74
99
|
raise "FMOD error! #{result}" if result != :FMOD_OK
|
data/lib/fmod/sound.rb
CHANGED
@@ -7,31 +7,41 @@ module FMOD
|
|
7
7
|
@system_pointer = FMOD.system.pointer
|
8
8
|
@channel = options[:channel] || Channel.new
|
9
9
|
@file = file
|
10
|
+
@sound_ptr = nil
|
10
11
|
end
|
11
|
-
|
12
|
-
def
|
13
|
-
|
12
|
+
|
13
|
+
def length(unit = FMOD_TIMEUNIT_MS)
|
14
|
+
FFI::MemoryPointer.new(:int) do |ptr|
|
15
|
+
error_check FMOD_Sound_GetLength(sound_ptr, ptr, FMOD_TIMEUNIT_MS)
|
16
|
+
return ptr.read_int
|
17
|
+
end
|
14
18
|
end
|
15
19
|
|
16
|
-
def
|
17
|
-
|
18
|
-
# FMOD_RESULT = FMOD_System_CreateStream(system, "wave.mp3", FMOD_DEFAULT, 0, &sound);
|
19
|
-
error_check FMOD_System_CreateStream(@system_pointer.read_pointer, @file, FMOD_DEFAULT, 0, sound_ptr);
|
20
|
-
|
20
|
+
def play(opts={})
|
21
|
+
opts = opts.keyword_args(:paused)
|
21
22
|
# FMOD_RESULT = FMOD_System_PlaySound(system, FMOD_CHANNEL_FREE, sound1, 0, &channel);
|
22
|
-
error_check FMOD_System_PlaySound(
|
23
|
+
error_check FMOD_System_PlaySound(FMOD.system.pointer, :FMOD_CHANNEL_FREE, sound_ptr, opts.paused ? 1 : 0, @channel.pointer_addr);
|
23
24
|
true
|
24
25
|
end
|
25
|
-
|
26
|
-
def
|
27
|
-
|
26
|
+
|
27
|
+
def release
|
28
|
+
return unless @sound_ptr
|
29
|
+
error_check FMOD_Sound_Release(@sound_ptr)
|
30
|
+
@sound_ptr = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def sound_ptr
|
36
|
+
return @sound_ptr if @sound_ptr
|
37
|
+
|
38
|
+
@memory = FFI::MemoryPointer.new(:pointer)
|
28
39
|
# FMOD_RESULT = FMOD_System_CreateSound(system, "drumloop.wav", FMOD_SOFTWARE, 0, &sound);
|
29
|
-
error_check FMOD_System_CreateSound(@system_pointer
|
40
|
+
error_check FMOD_System_CreateSound(@system_pointer, @file, FMOD_DEFAULT, 0, @memory);
|
30
41
|
|
31
|
-
|
32
|
-
error_check FMOD_System_PlaySound(@system_pointer.read_pointer, FMOD_CHANNELINDEX[:FMOD_CHANNEL_FREE], sound_ptr.read_pointer, 0, @channel.pointer);
|
33
|
-
true
|
42
|
+
@sound_ptr = @memory.read_pointer
|
34
43
|
end
|
44
|
+
|
35
45
|
|
36
46
|
end
|
37
|
-
end
|
47
|
+
end
|
data/lib/fmod/system.rb
CHANGED
@@ -3,51 +3,91 @@ module FMOD
|
|
3
3
|
include Functions
|
4
4
|
# pointer to the system instance
|
5
5
|
attr_reader :pointer
|
6
|
+
attr_reader :sample_rate
|
6
7
|
|
7
|
-
def initialize
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
@
|
12
|
-
|
13
|
-
|
8
|
+
def initialize(opts={})
|
9
|
+
opts = opts.keyword_args(:output_wav,
|
10
|
+
:sample_rate => 44100)
|
11
|
+
|
12
|
+
@sample_rate = opts.sample_rate
|
13
|
+
|
14
|
+
@memory = FFI::MemoryPointer.new(:pointer)
|
15
|
+
error_check FMOD_System_Create(@memory)
|
16
|
+
@pointer = @memory.read_pointer
|
17
|
+
|
18
|
+
case
|
19
|
+
when opts.output_wav
|
20
|
+
error_check FMOD_System_SetOutput(@pointer, :FMOD_OUTPUTTYPE_WAVWRITER_NRT)
|
21
|
+
error_check FMOD_System_SetSoftwareFormat(@pointer, sample_rate, :FMOD_SOUND_FORMAT_PCM16, 2, 6, :FMOD_DSP_RESAMPLER_LINEAR)
|
22
|
+
error_check FMOD_System_Init(@pointer, 100, FMOD_INIT_STREAM_FROM_UPDATE, opts.output_wav)
|
23
|
+
else
|
24
|
+
error_check FMOD_System_Init(@pointer, 32, FMOD_INIT_NORMAL, nil)
|
25
|
+
end
|
14
26
|
end
|
15
27
|
|
16
28
|
def version
|
17
29
|
# unsigned int version;
|
18
30
|
# result = FMOD_System_GetVersion(system, &version);
|
19
|
-
FFI::MemoryPointer.new(:int)
|
20
|
-
error_check FMOD_System_GetVersion(@pointer
|
31
|
+
FFI::MemoryPointer.new(:int) { |version_ptr|
|
32
|
+
error_check FMOD_System_GetVersion(@pointer, version_ptr);
|
21
33
|
return version_ptr.read_int
|
22
|
-
|
34
|
+
}
|
23
35
|
end
|
24
36
|
|
25
|
-
|
26
37
|
def driver_count
|
27
38
|
# unsigned int numdrivers;
|
28
39
|
# result = FMOD_System_GetNumDrivers(system, &numdrivers);
|
29
|
-
|
30
|
-
|
31
|
-
|
40
|
+
FFI::MemoryPointer.new(:int) { |count_ptr|
|
41
|
+
error_check FMOD_System_GetNumDrivers(@pointer, count_ptr)
|
42
|
+
count_ptr.read_int
|
43
|
+
}
|
32
44
|
end
|
33
45
|
|
34
46
|
def software_channels
|
35
|
-
|
36
|
-
|
37
|
-
|
47
|
+
FFI::MemoryPointer.new(:int) { |num_ptr|
|
48
|
+
error_check FMOD_System_GetSoftwareChannels(@pointer, num_ptr)
|
49
|
+
num_ptr.read_int
|
50
|
+
}
|
38
51
|
end
|
39
52
|
|
40
53
|
def hardware_channels
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
54
|
+
FFI::MemoryPointer.new(:int) { |num2d|
|
55
|
+
FFI::MemoryPointer.new(:int) { |num3d|
|
56
|
+
FFI::MemoryPointer.new(:int) { |total|
|
57
|
+
error_check FMOD_System_GetHardwareChannels(@pointer, num2d, num3d, total)
|
58
|
+
|
59
|
+
{
|
60
|
+
:num2d => num2d.read_int,
|
61
|
+
:num3d => num3d.read_int,
|
62
|
+
:total => total.read_int,
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
50
66
|
}
|
51
67
|
end
|
68
|
+
|
69
|
+
def dsp_clock
|
70
|
+
FFI::MemoryPointer.new(:int) { |hi|
|
71
|
+
FFI::MemoryPointer.new(:int) { |lo|
|
72
|
+
error_check FMOD_System_GetDSPClock(@pointer, hi, lo)
|
73
|
+
return Convert.int32_to_64(hi.read_int, lo.read_int)
|
74
|
+
}
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def buffer_size
|
79
|
+
FFI::MemoryPointer.new(:int) { |size|
|
80
|
+
error_check FMOD_System_GetDSPBufferSize(@pointer, size, nil)
|
81
|
+
return size.read_int
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
def update
|
86
|
+
error_check FMOD_System_Update(@pointer)
|
87
|
+
end
|
88
|
+
|
89
|
+
def release
|
90
|
+
error_check FMOD_System_Release(@pointer)
|
91
|
+
end
|
52
92
|
end
|
53
|
-
end
|
93
|
+
end
|
data/lib/fmod/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fmod-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-12-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|
16
|
-
requirement: &
|
16
|
+
requirement: &70344468616080 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,18 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70344468616080
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: hash_keyword_args
|
27
|
+
requirement: &70344468151240 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70344468151240
|
25
36
|
description: Wrapper for the FMOD audio library
|
26
37
|
email:
|
27
38
|
- ronen@barzel.org
|
@@ -33,17 +44,27 @@ files:
|
|
33
44
|
- Gemfile
|
34
45
|
- README.md
|
35
46
|
- Rakefile
|
47
|
+
- examples/ComputerMagic.mp3
|
48
|
+
- examples/ComputerMagic.wav
|
49
|
+
- examples/Flute3.wav
|
50
|
+
- examples/concat.rb
|
51
|
+
- examples/output/concat.wav
|
52
|
+
- examples/output/write.wav
|
53
|
+
- examples/play.rb
|
54
|
+
- examples/play2.rb
|
55
|
+
- examples/scratch.rb
|
56
|
+
- examples/volume.rb
|
57
|
+
- examples/write.rb
|
36
58
|
- fmod-ruby.gemspec
|
37
59
|
- lib/fmod.rb
|
38
60
|
- lib/fmod/channel.rb
|
39
61
|
- lib/fmod/constants.rb
|
62
|
+
- lib/fmod/convert.rb
|
40
63
|
- lib/fmod/enums.rb
|
41
64
|
- lib/fmod/functions.rb
|
42
|
-
- lib/fmod/pointer_helper.rb
|
43
65
|
- lib/fmod/sound.rb
|
44
66
|
- lib/fmod/system.rb
|
45
67
|
- lib/fmod/version.rb
|
46
|
-
- scratch.rb
|
47
68
|
homepage: https://github.com/ronen/fmod-ruby
|
48
69
|
licenses: []
|
49
70
|
post_install_message:
|
@@ -64,7 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
85
|
version: '0'
|
65
86
|
requirements: []
|
66
87
|
rubyforge_project:
|
67
|
-
rubygems_version: 1.8.
|
88
|
+
rubygems_version: 1.8.12
|
68
89
|
signing_key:
|
69
90
|
specification_version: 3
|
70
91
|
summary: Wrapper for the FMOD audio library
|