Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

FMOD Ex Audio Module Rewrite

panchokoster":1i5zrds8 said:
great script!, but I got an error in battle (even in the demo) it says 'FMOD Ex returned error 58' it seems to be related to the animation_process_timing method, I got the error just defending with all the battlers a few times.
there are also argument errors with some scripts when they say something like Audio.se_play('se_name'),
but you can fix it just changing the line 1257 of your script to
'def Audio.se_play(filename, volume = 100, pitch = 100) ', right?

Thanks for your input. I fixed the argument error with se_play and me_play. As for the other error, it's a problem with your sound card or driver. I've changed the script to use a software mixer instead of relying on the sound card itself, which should solve most hardware problems. I updated the first post with a new version of the script with these changes, so could you download the new version and see if you still get that error?
 
I went into the skills menu and mashed directions and the bgm cut out. I made it start playing again by talking to one of the people, went back into skills and did it again, and got an error.

Script 'FmodEx *' line 312: RuntimeError occured.
FMOD Ex returned error 36

Looks like a great script though, should add a lot of functionality.
Do any of the formats supported so far work with sound fonts, or do you plan to get it working with any that do?
 
Hmm...
It seems the problem is that Scene_Menu doesn't call $game_system.update (which calls Audio.update to stop and release any finished Sound Effects), and since SEs aren't cleaned probably you end up with more SEs than could be handled. One way to solve that is to add a $game_system.update to Scene_Menu's (and every other menu) update method:

  def update
    $game_system.update
    @command_window.update
......
......

I don't think this is the best way for the script to handle that though. I'll try to fix it in the next version (maybe clean up each time you play an SE, or even running Audio.update in a separate thread.. I'm not sure)

I think FMOD Ex can handle sound fonts, I don't know much about sound fonts and I have other priorities at the moment. Maybe you could go to FMOD's website, download the documentation, study the script, and try to add sound font on your own!
 
BwdYeti":17r08req said:
I went into the skills menu and mashed directions and the bgm cut out. I made it start playing again by talking to one of the people, went back into skills and did it again, and got an error.

Script 'FmodEx *' line 312: RuntimeError occured.
FMOD Ex returned error 36

Ok, I updated a new version that should fix that. Could you try it and see if you still get that problem?

I suggest everyone download and use the new version. If you're already using it in a project don't forget to overwrite my old Game_System script section (the one at the bottom of script editor list in demo) with the new one as well as the FModEx script section as they were both modified.
 
Well, not directly. RMXP default Audio class recognizes control change 111 event in a MIDI and uses it as a loop start point. I'm not sure how to do that with FMOD, though I do plan on supporting something similar for OGGs (using tags). For now you can do it manually; Right after you play the BGM, add this script FMod::bgm_set_loop_points(start, -1) where start is the start of the part after the intro (and you can change the -1 to specify end of loop too, -1 means the end of the loop is the end of the BGM) in milliseconds (1 second = 1000 milliseconds), so if you figure out where exactly the loop start happens you could emulate that feature.

I'm not sure if FMOD has direct access to MIDI events but I'll look into it. The more manual alternative should suffice for now, at least.
 
The ability to resume music tracks from an arbitrary point sounds NUMBER ONE!

How much work might I expect to do to make this compatible with VX? (keeping in mind I'm very fluent with scripting) I wouldn't expect much, but I don't have access to the source from where I am right now.
 

derula

Member

Hi. First: Great script!

Cowlol":3i2aq27b said:
Well, not directly. RMXP default Audio class recognizes control change 111 event in a MIDI and uses it as a loop start point. I'm not sure how to do that with FMOD, though I do plan on supporting something similar for OGGs (using tags).

I've been doing an audio replacement for RM2k a while ago, and the main problem about the midis is not only the controller thing but also that it skips the beginning of any Midi in which no "note on"-events are found. I haven't found anything that was able to reproduce that behavior, and I also took a look at FMOD. But that maybe only means that I'm too stupid ;)

For this Audio replacement, I wrote a tool which allowed you to find, preview and set a starting point for loops in MP3, WMA and OGG files. This info was saved in the comment tag of each format, prepended by "DisharmonyLoop:". But I think it was saved in centiseconds, not microseconds. If the user chose to not play the file looped, the comment tag was set to "DisharmonyLoop:no loop". Maybe I could recycle this tool to help you a little.

I would be very happy if you were able to get midi playback right the same way the Maker is doing it.

(This post editor is highly irritating.)
 
I've found a small side effect of this script. Usually you can use the Play BGM event to play an AVI or MPG video, if ungracefully (unless you use the Scene_Movie script), but this script doesn't recognize those extensions so you get a file not found error. I tried adding the extensions to the array but that caused a different error and I'm not too confident in my ability to debug it. I'm going to give it a try in a few weeks, but if anyone else wants to save me the effort it would be greatly appreciated. Movies and music make an excellent combination.
 
Frieza2000":2i6xaa66 said:
I've found a small side effect of this script. Usually you can use the Play BGM event to play an AVI or MPG video, if ungracefully (unless you use the Scene_Movie script), but this script doesn't recognize those extensions so you get a file not found error. I tried adding the extensions to the array but that caused a different error and I'm not too confident in my ability to debug it. I'm going to give it a try in a few weeks, but if anyone else wants to save me the effort it would be greatly appreciated. Movies and music make an excellent combination.

FMOD is an audio library, it doesn't play video files of any kind. Making it plays video is akin to writing a brand new script that wraps some video library, which isn't the scope of this script.

So I guess you'll either have to look for a video playing script that would work with this or use RMXP's default Audio module. Sorry.
 
Frieza2000":15flsb6h said:
What if I just rename the standard Audio class and have the movie script call that? Can it be run concurrently with FMOD?
If I can recall, standard Audio module is built-in, and hidden from end users.  Audiofile class is also hidden, but at least its definition is provided in help file.
 
Here's something weird. I see that the bgm_play method has a parameter to determine whether or not the music will loop (an awsome feature), and it's default value is true.

Code:
def self.bgm_play(name, volume, pitch, position = 0, looping = true)

I called this script event:
Code:
Audio.bgm_play('Audio/BGM/' + 
'Hooray beer',100,100,0,false)

But no matter what I passed in for the 5th argument, it still looped. Oddly, I found that if I passed in an integer or a string instead of true or false, the music would fade in over the span of about 3 seconds (which is kind of neat because I don't know of another way to do that). Changing the default value to false stops it from looping, but then nothing loops no matter what you pass in. Right now I'm using a global variable as the default value, and that lets me dynamically change whether it loops or not.

Code:
def self.bgm_play(name, volume, pitch, position = 0, looping = $fmod_looping)

$fmod_looping = false
Audio.bgm_play('Audio/BGM/' + 
'Hooray beer',100,100,0)

I don't see why the parameter approach doesn't work, though. Am I passing it in wrong?
 
Code:
def self.bgm_play(name, volume, pitch, position = 0, looping = true)

is part of the FMod module, which the rewritten Audio module is based on. The one used by the Audio module is farther down in the script:

Code:
def Audio.bgm_play(filename, volume = 100, pitch = 100, position = 0, fade_in = false)

which explains why the last parameter made it fade in (fade in length is specified by the parameter BGM_FADE_IN_INCREMENT, which by default increases volume by 5 each 0.2 seconds). Basically, Audio.bgm_play calls FMod.bgm_play, and does additional stuff like fading and other things the default Audio module did.The reason I didn't allow specifying looping here is because the Audio.me_play is better for playing something once. If you really need it, feel free to call FMod::bgm_play(filename, start_volume, pitch, position, looping) directly or change the definition of Audio.bgm_play so that it takes an extra parameter and uses it for calling FMod::bgm_play, something like:

  def Audio.bgm_play(filename, volume = 100, pitch = 100, position = 0,
                      fade_in = false, loop = true)
    if @bgm_fading_out and !fade_in
      @next_bgm = RPG::AudioFile.new(filename, volume, pitch)
      @next_bgm_position = position
      return
    end
    start_volume = volume
    if fade_in
      @bgm_target_volume = volume unless @bgm_fading_in
      @bgm_fading_in = true
      start_volume = 0
    end
    @bgm_fading_out = false
    # If a ME is playing we wait until it's over before playing BGM
    unless @me_playing
      FMod::bgm_play(filename, start_volume, pitch, position, loop)
    end
    @playing_bgm = RPG::AudioFile.new(filename, volume, pitch)
    @memorized_bgm = @playing_bgm
    @memorized_bgm_position = position
  end
 
You can't assign start position with MEs though, and there's one part where I just want to play the end of a song. This saves me from having to make two files for the same song.


I got movies to work! Just rename Fmod's audio module and the functions it overwrites. I only did it for the BGS functions because I plan on playing the movies as BGSs, but here's the changes for both BGM and BGS:

Code:
==FmodEx *==
#module Audio
 module FAudio

#def Audio.bgm_play(filename, volume = 100, pitch = 100, position = 0,
 def FAudio.bgm_play(filename, volume = 100, pitch = 100, position = 0,

#def Audio.bgm_stop
 def FAudio.bgm_stop

#def Audio.bgs_play(filename, volume = 100, pitch = 100, position = 0)
 def FAudio.bgs_play(filename, volume = 100, pitch = 100, position = 0)

#def Audio.bgs_stop
 def FAudio.bgs_stop

Then rename the calls to them in Game_System:

==Game_System *==

#Audio.bgm_play("Audio/BGM/" + bgm.name, bgm.volume, bgm.pitch, pos)
 Audio.bgm_play("Audio/BGM/" + bgm.name, bgm.volume, bgm.pitch, pos)

#Audio.bgm_stop
 FAudio.bgm_stop

#Audio.bgs_play("Audio/BGS/" + bgs.name, bgs.volume, bgs.pitch, pos)
 FAudio.bgs_play("Audio/BGS/" + bgs.name, bgs.volume, bgs.pitch, pos)

#Audio.bgs_stop
 FAudio.bgs_stop

#Audio.bgs_stop
 FAudio.bgs_stop

This creates two audio modules which can be used simultaneously! The standard play BGM/BGS events will still be mapped to the FMod module as the script originally intended, but now you have the option of directly calling the old audio module. For example:

Code:
Audio.bgs_play('Audio/BGM/' + 
'The Dance Of Eternity',100,100)
FMod::bgs_play('Audio/BGS/' + 
'OOT_AfterBoss_Glow',100,100,start_position)

This will result in two BGSs playing at once! Naturally, you can't use the FMod features like assigning a start position, setting loop points, or playing the extra file formats with the old module. The FMod module can be controlled with the standard play/stop, but the old audio module must be stopped with Audio.bgs_stop. The fade out events will effect both modules.

Sample movie call:

Code:
Audio.bgs_play('Movies/' + 
'Inferno Punch',100,100)
Audio.bgs_stop
$scene = Scene_Movie.close_scene
$scene=Scene_Movie.new('Inferno P' +
'unch')
$scene = Scene_Map.new


I also found that the memorize/restore events don't work if you start the audio from a script event (with either Audio.bgm_play or FMod::bgm_play). Restore will play the audio file that you last started with an event from the position that you last memorized regardless of what song may have actually been playing when you memorized it because @playing_bgs (Game_System) doesn't get set up when you start your audio from a script event. I'm not sure if there's a way to directly assign the class variable, but a simple way to deal with it is to start the audio with an event immediately before using the script event so that the name of the correct file is in memory. Note that volume and pitch are also memorized from the play event.

Code:
@>Play BGM: 'The Dance Of Eternity',100,100
@>Script: FMod::bgm_play('Audio/BGM/' + 
  'The Dance Of Eternity',100,100,
  start_position,false)

This is fine in most situations but unfortunately this method overrides the last parameter, so even though it's 'false' it will still loop. Another way is to memorize and restore the position manually.

Code:
@>Script: Audio.bgm_play('Audio/BGM/' + 
  'The Dance Of Eternity',100,100,
  start_position)
@>Text: Wait
@new_start_position = FMod::bgm_position
@>Text: Memorized
@>Script: Audio.bgm_play('Audio/BGM/' + 
  'The Dance Of Eternity',100,100,
@new_start_position)
@>Text: Restored

Thanks again for this indispensable script. Without the ability to set loop points I probably would've abandoned RPG Maker in favor of something else.
 
wow...This script is very useful indeed! However, I've run into a problem since I've begun using it. The script works fine and has no errors when I use the game at my house. However, when I use it at school and such I get an error with the script. The error occurs on something like line 736 or around there (I can't test it from home but I'll try to confirm this tomorrow.) I'm pretty sure that the error is a no method for 'chr'. It only happens on comps that don't have the RTP installed so I dunno what the problem is. The game does work with out that script on comps without the RTP but that script give me the error. Any ideas why???


Also, I'll double check tomorrow around 11 or so and update this post as to what the actual line of code is. Thanks for the help!

~xgamexfreakx
 
Yeah, I could see why that happens. The script assumes the RTP folder exists when it doesn't. I'll upload a fixed version soon. For now you could change selectBGMFilename on line 760 to this*:

Code:
  def self.selectBGMFilename(name)
    name = name.gsub("/", "\\")
    # See if file exists in game folder
    localname = self.checkExtensions(name, FModEx::FMOD_FILE_TYPES)
    if FileTest.exist?(localname)
      return localname
    end
    # See if file exists in RTP
    commonname = self.checkExtensions(getRTPFolder + name, FModEx::FMOD_FILE_TYPES)
    if FileTest.exist?(commonname)
      return commonname
    end
    # An invalid name was provided
    return name
  end

* An ugly fix because it'll still give you the same error if file doesn't exist in game folder on a PC without RTP, but since you're supposed to get a "file not found" error when that happens anyway, it should work fine (with a cryptic error message). The proper fix will address this... when I get to it.
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top