S. F. LaValle
Member
Hiya all,
I'm using the awesome binding script Janus wrote to use FMOD Ex within RGSS. He wrote it only to allow FMOD's playback options (file formats) to be used for your game bgm's (not BGS, ME, or SE yet). Well, I'm looking to expand on this.
Seeing as I had to learn programming from scratch, things are going decently well, or were until I hit a snag. When you create an FMOD object, it assigns them a "handle". My problem lies with when I need to interact with FMOD. When I try to pass this handle back to FMOD to find an FMOD object I created, it cannot find it.
Here is the full code, written by Janus, with my additions:
The parts that I added to the above code are the following: DSP class and all its functions, createDSPByType in the System class, and addDSP in the Channel class.
With Janus's original code, he implemented a way to store a handle for the System, Sound, and Channel objects. Here's an example from the createStream function:
The variable 'temp' now contains FMOD's native pointer to the object. He also created a method in which to extract the contents of the variable into a fixnum, so that it can be used within RGSS. He assigns each object this handle, but I'm not sure that it's ever used.
My problem is that I *do* need to use this handle. Once I've created a DSP object via createDSPByType function in the system class, I need to use the addDSP function in the channel class to attach the DSP object to that channel, thus filtering the noise.
I've determined that I am successful with creating the DSP object, as I've gotten a result of FMOD_OK (FMOD's return on a function that succeeds) for the createDSPByType. When I tried to pass the handle that is generated for this DSP object into the addDSP function I call for the playing channel, I get a return code from FMOD, FMOD_ERR_DSP_NOTFOUND. I have tried passing this handle in a few ways; I've tried using Janus's way, which is to use the 'temp' variable after FMOD has filled it with a pointer, "unpacking" that into an integer, then re-packing it when the addDSP function is called. I've also tried skipping the packing/unpacking process, setting a global variable as soon as 'temp' is popped out with FMOD's object handle, then plugging that same variable right back in without modifying it.
Either way, I can't help but feel it is a translation issue between RGSS and FMOD. FMOD has objects created, but I can't reference them. Anyone have any ideas?
If I can overcome that obstacle, I don't foresee anything in my way to extending functionality into RGSS for all of FMOD's DSP effects, not to mention setting up BGS, ME, and SE to all function through FMOD as well. Thanks for looking!
I'm using the awesome binding script Janus wrote to use FMOD Ex within RGSS. He wrote it only to allow FMOD's playback options (file formats) to be used for your game bgm's (not BGS, ME, or SE yet). Well, I'm looking to expand on this.
Seeing as I had to learn programming from scratch, things are going decently well, or were until I hit a snag. When you create an FMOD object, it assigns them a "handle". My problem lies with when I need to interact with FMOD. When I try to pass this handle back to FMOD to find an FMOD object I created, it cannot find it.
Here is the full code, written by Janus, with my additions:
Code:
# FMod Ex
# binding by Kevin Gadd (janus@luminance.org)
module FModEx
FMOD_INIT_NORMAL = 0
FMOD_OK = 0
FMOD_ERR_FILE_NOT_FOUND = 23
FMOD_ERR_INVALID_HANDLE = 36
FMOD_ERR_DSP_NOTFOUND = 16
FMOD_DEFAULT = 0
FMOD_LOOP_OFF = 1
FMOD_LOOP_NORMAL = 2
FMOD_LOOP_BIDI = 4
FMOD_LOOP_BITMASK = 7
FMOD_2D = 8
FMOD_3D = 16
FMOD_HARDWARE = 32
FMOD_SOFTWARE = 64
FMOD_CREATESTREAM = 128
FMOD_CREATESAMPLE = 256
FMOD_OPENUSER = 512
FMOD_OPENMEMORY = 1024
FMOD_OPENRAW = 2048
FMOD_OPENONLY = 4096
FMOD_ACCURATETIME = 8192
FMOD_MPEGSEARCH = 16384
FMOD_NONBLOCKING = 32768
FMOD_UNIQUE = 65536
FMOD_DSP_TYPE_REVERB = 13
FMOD_CHANNEL_FREE = -1
FMOD_CHANNEL_REUSE = -2
FMOD_FILE_TYPES = ["ogg", "aac", "wma", "mp3", "wav", "it", "xm", "mod", "s3m", "mid", "midi"]
# class that manages importing functions from the DLL
class DLL
attr_accessor :filename
attr_accessor :functions
def initialize(filename = "fmodex.dll")
@filename = filename # store DLL filename for instance
@functions = {} # empty hashtable
@handle = 0
# load library
w32_LL = Win32API.new("kernel32.dll", "LoadLibrary", 'p', 'l')
@handle = w32_LL.call(filename)
self.import("System_Create", 'p')
self.import("System_Init", 'llll')
self.import("System_Close", 'l')
self.import("System_Release", 'l')
self.import("System_CreateSound", 'lpllp')
self.import("System_CreateStream", 'lpllp')
self.import("System_PlaySound", 'llllp')
self.import("System_CreateDSPByType", 'llp')
self.import("DSP_GetType", 'lp')
self.import("Sound_Release", 'l')
self.import("Sound_GetMode", 'lp')
self.import("Sound_SetMode", 'll')
self.import("Channel_Stop", 'l')
self.import("Channel_IsPlaying", 'lp')
self.import("Channel_GetPaused", 'lp')
self.import("Channel_SetPaused", 'll')
self.import("Channel_GetVolume", 'lp')
self.import("Channel_SetVolume", 'll')
self.import("Channel_GetPan", 'lp')
self.import("Channel_SetPan", 'll')
self.import("Channel_GetFrequency", 'lp')
self.import("Channel_SetFrequency", 'll')
self.import("Channel_AddDSP", 'lp')
end
def import(name, args = '', returnType = 'l')
@functions[name] = Win32API.new(@filename, "FMOD_" + name, args, returnType)
end
def [](key)
return @functions[key]
end
def invoke(name, *args)
fn = @functions[name]
raise "function not imported: #{name}" if fn.nil?
result = fn.call(*args)
if result == FMOD_OK
# raise "success"
else
# raise "FMod Ex returned error #{result}"
# error
end
return result
end
def convertFloat(f)
temp = [f].pack('f')
return unpackInt(temp)
end
def unpackInt(s)
return s.unpack('l')[0]
end
def unpackFloat(s)
return s.unpack('f')[0]
end
def unpackBool(s)
return s.unpack('l')[0] == 0 ? false : true
end
def dispose
@handle
end
end
# class that manages an instance of FMOD::System
class System
attr_accessor :fmod
attr_accessor :handle
def initialize(theDLL, maxChannels = 32, flags = FMOD_INIT_NORMAL, extraDriverData = 0)
@fmod = theDLL
temp = 0.chr * 4
@fmod.invoke("System_Create", temp)
@handle = @fmod.unpackInt(temp)
@fmod.invoke("System_Init", @handle, maxChannels, flags, extraDriverData)
end
def createSound(filename, mode = FMOD_DEFAULT)
temp = 0.chr * 4
result = @fmod.invoke("System_CreateSound", @handle, filename, mode, 0, temp)
raise "File not found: #{filename}!" if result == FMOD_ERR_FILE_NOT_FOUND
newSound = Sound.new(self, @fmod.unpackInt(temp))
return newSound
end
def createStream(filename, mode = FMOD_DEFAULT)
temp = 0.chr * 4
result = @fmod.invoke("System_CreateStream", @handle, filename, mode, 0, temp)
#print "File not found: #{filename}!" if result == FMOD_ERR_FILE_NOT_FOUND
newSound = Sound.new(self, @fmod.unpackInt(temp))
return newSound
end
def createDSPByType(type = FMOD_DSP_TYPE_REVERB)
temp = 0.chr * 4
result = @fmod.invoke("System_CreateDSPByType", @handle, type, 01.to_s)
print result
newDSP = DSP.new(self, @fmod.unpackInt(temp))
return newDSP
end
def dispose
if (@handle > 0)
@fmod.invoke("System_Close", @handle)
@fmod.invoke("System_Release", @handle)
@handle = 0
end
@fmod = nil
end
end
# class that manages an instance of FMOD::DSP
class DSP
attr_accessor :system
attr_accessor :handle
attr_accessor :fmod
def initialize(theSystem, theHandle)
@system = theSystem
@fmod = theSystem.fmod
@handle = theHandle
end
def getType
temp = 0.chr * 4
result = @fmod.invoke("DSP_GetType", @handle, temp)
end
end
# class that manages an instance of FMOD::Sound
class Sound
attr_accessor :system
attr_accessor :fmod
attr_accessor :handle
def initialize(theSystem, theHandle)
@system = theSystem
@fmod = theSystem.fmod
@handle = theHandle
end
def play(paused = false, channel = nil)
if (channel == nil)
temp = 0.chr * 4
else
temp = [channel].pack('l')
end
@fmod.invoke("System_PlaySound", @system.handle, (channel == nil) ? FMOD_CHANNEL_FREE : FMOD_CHANNEL_REUSE, @handle, (paused == true) ? 1 : 0, temp)
theChannel = @fmod.unpackInt(temp)
newChannel = Channel.new(self, theChannel)
return newChannel
end
def mode
temp = 0.chr * 4
@fmod.invoke("Sound_GetMode", @handle, temp)
return @fmod.unpackInt(temp)
end
def mode=(newMode)
@fmod.invoke("Sound_SetMode", @handle, newMode)
end
def loopMode
temp = 0.chr * 4
@fmod.invoke("Sound_GetMode", @handle, temp)
return @fmod.unpackInt(temp) & FMOD_LOOP_BITMASK
end
def loopMode=(newMode)
@fmod.invoke("Sound_SetMode", @handle, (self.mode() & ~FMOD_LOOP_BITMASK) | newMode)
end
def dispose
if (@handle > 0)
@fmod.invoke("Sound_Release", @handle)
@handle = 0
end
@fmod = nil
@system = nil
end
end
# class that represents an FMOD::Channel
class Channel
attr_accessor :system
attr_accessor :sound
attr_accessor :fmod
attr_accessor :handle
def initialize(theSound, theHandle)
@sound = theSound
@system = theSound.system
@fmod = theSound.system.fmod
@handle = theHandle
end
def stop
@fmod.invoke("Channel_Stop", @handle)
end
def valid?
temp = 0.chr * 4
result = @fmod.invoke("Channel_IsPlaying", @handle, temp)
return false if (result == FMOD_ERR_INVALID_HANDLE)
return true
end
def playing?
temp = 0.chr * 4
@fmod.invoke("Channel_IsPlaying", @handle, temp)
return @fmod.unpackBool(temp)
end
def volume
temp = 0.chr * 4
@fmod.invoke("Channel_GetVolume", @handle, temp)
return @fmod.unpackFloat(temp)
end
def volume=(newVolume)
@fmod.invoke("Channel_SetVolume", @handle, @fmod.convertFloat(newVolume))
end
def pan
temp = 0.chr * 4
@fmod.invoke("Channel_GetPan", @handle, temp)
return @fmod.unpackFloat(temp)
end
def pan=(newPan)
@fmod.invoke("Channel_SetPan", @handle, @fmod.convertFloat(newPan))
end
def frequency
temp = 0.chr * 4
@fmod.invoke("Channel_GetFrequency", @handle, temp)
return @fmod.unpackFloat(temp)
end
def frequency=(newFrequency)
@fmod.invoke("Channel_SetFrequency", @handle, @fmod.convertFloat(newFrequency))
end
def paused
temp = 0.chr * 4
@fmod.invoke("Channel_GetPaused", @handle, temp)
return @fmod.unpackBool(temp)
end
def paused=(newPaused)
@fmod.invoke("Channel_SetPaused", @handle, (newPaused == true) ? 1 : 0)
end
def addDSP(dspobject)
temp = [dspobject].pack('l')
result = @fmod.invoke("Channel_AddDSP", @handle, temp)
print "boo" if result == FMOD_ERR_DSP_NOTFOUND
end
def dispose
@handle = 0
@sound = nil
@system = nil
@fmod = nil
end
end
end
module Win32
CSIDL_PROGRAM_FILES_COMMON = 43
end
$fmod_dll = FModEx::DLL.new
$fmod = FModEx::System.new($fmod_dll)
def getRGSSFolder()
getFL = Win32API.new("shell32.dll", "SHGetSpecialFolderLocation", 'llp', 'l')
temp = 0.chr * 4
getFL.call(0, Win32::CSIDL_PROGRAM_FILES_COMMON, temp)
pidl = temp.unpack('l')[0]
getFP = Win32API.new("shell32.dll", "SHGetPathFromIDList", 'lp', 'l')
buf = 32.chr * 260
result = getFP.call(pidl, buf)
if (result > 0)
buf = buf.slice(0,buf.index(0.chr))
return buf + "\\Enterbrain\\RGSS\\Standard\\"
end
end
def checkExtensions(name, extensions)
if FileTest.exist?(name)
return name
end
extensions.each do |ext|
if FileTest.exist?(name + "." + ext)
return name + "." + ext
end
end
return name
end
def selectBGMFilename(name)
name = name.gsub("/", "\\")
localname = checkExtensions(name, FModEx::FMOD_FILE_TYPES)
commonname = checkExtensions(getRGSSFolder() + name, FModEx::FMOD_FILE_TYPES)
if FileTest.exist?(localname)
return localname
end
if FileTest.exist?(commonname)
return commonname
end
return name
end
def FMod_bgm_play(name, volume, pitch)
filename = selectBGMFilename(name)
if ($fmod_bgm_name == filename)
return
end
FMod_bgm_stop()
$fmod_bgm_name = filename
# load the sound as a stream
$fmod_bgm = $fmod.createStream(filename)
$fmod_bgm.loopMode = FModEx::FMOD_LOOP_NORMAL
$fmod_bgm_reverb = $fmod.createDSPByType()
$fmod_bgm_channel = $fmod_bgm.play(false, 100)
# apply volume and pitch
$fmod_bgm_channel.volume = volume / 100
$fmod_bgm_channel.frequency = $fmod_bgm_channel.frequency * pitch / 100
$fmod_bgm_channel.addDSP($fmod_bgm_reverb.handle)
$fmod_bgm_reverb.getType
end
def FMod_bgm_stop()
if $fmod_bgm_channel == nil
return
end
$fmod_bgm_channel.stop()
$fmod_bgm_channel = nil
# clean up the sound
$fmod_bgm.dispose()
$fmod_bgm = nil
$fmod_bgm_name = nil
end
def FMod_bgm_fade(time)
# no easy way to make this work, so we just stop it
FMod_bgm_stop()
end
The parts that I added to the above code are the following: DSP class and all its functions, createDSPByType in the System class, and addDSP in the Channel class.
With Janus's original code, he implemented a way to store a handle for the System, Sound, and Channel objects. Here's an example from the createStream function:
Code:
temp = 0.chr * 4 #to create a blank pointer with null chars
result = @fmod.invoke("System_CreateStream", @handle, filename, mode, 0, temp) # temp is now passed into FMOD, where FMOD fills temp with a pointer to the created object
My problem is that I *do* need to use this handle. Once I've created a DSP object via createDSPByType function in the system class, I need to use the addDSP function in the channel class to attach the DSP object to that channel, thus filtering the noise.
I've determined that I am successful with creating the DSP object, as I've gotten a result of FMOD_OK (FMOD's return on a function that succeeds) for the createDSPByType. When I tried to pass the handle that is generated for this DSP object into the addDSP function I call for the playing channel, I get a return code from FMOD, FMOD_ERR_DSP_NOTFOUND. I have tried passing this handle in a few ways; I've tried using Janus's way, which is to use the 'temp' variable after FMOD has filled it with a pointer, "unpacking" that into an integer, then re-packing it when the addDSP function is called. I've also tried skipping the packing/unpacking process, setting a global variable as soon as 'temp' is popped out with FMOD's object handle, then plugging that same variable right back in without modifying it.
Either way, I can't help but feel it is a translation issue between RGSS and FMOD. FMOD has objects created, but I can't reference them. Anyone have any ideas?
If I can overcome that obstacle, I don't foresee anything in my way to extending functionality into RGSS for all of FMOD's DSP effects, not to mention setting up BGS, ME, and SE to all function through FMOD as well. Thanks for looking!