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 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 finished, but all the basic techniques for interfacing Ruby with the FMOD library are outlined. If you want to contribute to this project, please get in touch.
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). At the moment, only Mac OS X is supported, but adding Linux or Windows is likely as easy as pointing to the library locations.
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
@@ -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
@@ -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
@@ -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
- require 'lib/fmod'
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
- file = (ARGV.empty?) ? "Kids.mp3" : ARGV[0]
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
@@ -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
@@ -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
@@ -16,4 +16,5 @@ Gem::Specification.new do |gem|
16
16
  gem.version = Fmod::VERSION
17
17
 
18
18
  gem.add_dependency("ffi")
19
+ gem.add_dependency("hash_keyword_args")
19
20
  end
@@ -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/system'
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
@@ -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
- @system_pointer = FMOD.system.pointer
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(@pointer.read_pointer, ptr)
17
- return playing_ptr.read_int == 1
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(@pointer.read_pointer, ((paused?) ? 0 : 1))
23
- true
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(@pointer.read_pointer, ptr)
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(@pointer.read_pointer, int, FMOD_TIMEUNIT_MS)
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(@pointer.read_pointer, float)
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(@pointer.read_pointer, ptr)
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(@pointer.read_pointer, float)
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
@@ -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
@@ -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
@@ -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, :int, :pointer, :int, :pointer], FMOD_RESULT
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
@@ -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 play
13
- play_stream
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 play_sound
17
- sound_ptr = FFI::MemoryPointer.new(:pointer)
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(@system_pointer.read_pointer, FMOD_CHANNELINDEX[:FMOD_CHANNEL_FREE], sound_ptr.read_pointer, 0, @channel.pointer);
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 play_stream
27
- sound_ptr = FFI::MemoryPointer.new(:pointer)
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.read_pointer, @file, FMOD_DEFAULT, 0, sound_ptr);
40
+ error_check FMOD_System_CreateSound(@system_pointer, @file, FMOD_DEFAULT, 0, @memory);
30
41
 
31
- # FMOD_RESULT = FMOD_System_PlaySound(system, FMOD_CHANNEL_FREE, sound1, 0, &channel);
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
@@ -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
- # FMOD_SYSTEM *system;
9
- # result = FMOD_System_Create(&system);
10
- # result = FMOD_System_Init(system, 32, FMOD_INIT_NORMAL, NULL);
11
- @pointer = FFI::MemoryPointer.new(:pointer)
12
- error_check FMOD_System_Create(@pointer)
13
- error_check FMOD_System_Init(@pointer.read_pointer, 32, FMOD_INIT_NORMAL, nil)
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) do |version_ptr|
20
- error_check FMOD_System_GetVersion(@pointer.read_pointer, version_ptr);
31
+ FFI::MemoryPointer.new(:int) { |version_ptr|
32
+ error_check FMOD_System_GetVersion(@pointer, version_ptr);
21
33
  return version_ptr.read_int
22
- end
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
- count_ptr = FFI::MemoryPointer.new(:int)
30
- error_check FMOD_System_GetNumDrivers(@pointer.read_pointer, count_ptr)
31
- count_ptr.read_int
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
- num_ptr = FFI::MemoryPointer.new(:int)
36
- error_check FMOD_System_GetSoftwareChannels(@pointer.read_pointer, num_ptr)
37
- num_ptr.read_int
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
- num2d = FFI::MemoryPointer.new(:int)
42
- num3d = FFI::MemoryPointer.new(:int)
43
- total = FFI::MemoryPointer.new(:int)
44
- error_check FMOD_System_GetHardwareChannels(@pointer.read_pointer, num2d, num3d, total)
45
-
46
- {
47
- :num2d => num2d.read_int,
48
- :num3d => num3d.read_int,
49
- :total => total.read_int,
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
@@ -1,3 +1,3 @@
1
1
  module Fmod
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.1"
3
3
  end
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.0.1
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-11-16 00:00:00.000000000 Z
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: &70253800274800 !ruby/object:Gem::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: *70253800274800
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.10
88
+ rubygems_version: 1.8.12
68
89
  signing_key:
69
90
  specification_version: 3
70
91
  summary: Wrapper for the FMOD audio library
@@ -1,8 +0,0 @@
1
- module FMOD
2
- class PointerHelper
3
- def self.release(pointer)
4
-
5
- end
6
- end
7
-
8
- # p = AutoPointer.new(other_pointer, PointerHelper.method(:release))