Advertisement
mikb89

[VX] Audio Pump Up: FMOD Ex v1.4

Apr 25th, 2012
219
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 84.56 KB | None | 0 0
  1. # Audio Pump Up: FMOD Ex v. 1.4
  2. # VX version
  3. # by mikb89
  4.  
  5. # Details:
  6. #  Audio module rewrite using FMOD library (http://www.fmod.org/).
  7. #
  8. #  I was trying to develop this using MCI, but I ended up porting the XP version
  9. #   of the Cowlol's edited Hiretsukan's FMOD Ex Audio script.
  10. #
  11. #  This script is intended to offer enhanced audio capabilities, without
  12. #   replacing the standard ones. You'll have various audio channel that you can
  13. #   control with a variable. If variable is 0, controls are related to the 0
  14. #   channel, if variable is 1, controls are related to channel 1, etc.
  15. #  Channel 0 is the default audio system.
  16. #  Channel from 1 to the max number specified, works like the standard system
  17. #   (if you play another BGM, the previous one will stop) but you can play many
  18. #   audios, using many channels. Simple.
  19. #
  20. #  Do not exagerate with the max channel number! Use just the channels you need.
  21. #  Please notice that different audio channels means different audios of the same
  22. #   type (BGM or BGS or ME) playing also at the SAME TIME. You'll need about 3 or
  23. #   4 channels even if you're planning to create very special effects, so.
  24.  
  25. # Configurations:
  26. module APU
  27.   CURRENT_CHANNEL_VARIABLE = 1 # Which variable use to choose current channel?
  28.   MAX_CHANNELS = 4 # How many channels do you plan to have in your game?
  29.   READ_F1_SETTINGS = false # User can disable channels 1+ audio with F1?
  30.     # Note: in VX, F1 settings are written only after application closing. :(
  31. end
  32. # For a correct application of the script in VX, you should insert in the Main
  33. #  script a line for disposing the FMod module on exit. The code is just:
  34. # FMod::dispose
  35. #  and should be pasted in Main, before the "rescue" one.
  36.  
  37. # Others:
  38. #
  39. #  * Modified to be fully compatible with VX Ace
  40. #  * Modified to have multiple channels
  41. #  * Modified to optionally read F1 audio settings
  42. #  * Modified to support awesome audio effects (DSP, read documentation)
  43. #  * Modified to make the pan function (balance) usable
  44. #
  45. #  Differences with standard audio system (channel 0).
  46. #  In channel 1+:
  47. #   - MIDIs sound differently;
  48. #   - on memorize/replay event commands, the position is also memorized;
  49. #   - same SE file can be played more than once at the same time;
  50. #   - fading doesn't continue if game window isn't active.
  51.  
  52.  
  53. # Original script comment following:
  54. #==============================================================================
  55. # ** FMOD Ex Audio
  56. #------------------------------------------------------------------------------
  57. #  Script by            :   Hiretsukan (Kevin Gadd)
  58. #                           janus@luminance.org
  59. #  Modified by:         :   RPG/Cowlol (Firas Assad)
  60. #                           ArePeeGee (AIM name)
  61. #  Last Update          :   September 23rd, 2008
  62. #  Version              :   1.5
  63. #------------------------------------------------------------------------------
  64. # Usage:
  65. #
  66. # You need to copy the file fmodex.dll to your game folder (folder where
  67. # your Game.exe and project file are). I've provided the DLL with the
  68. # demo, and you can also get the latest version from FMOD's official
  69. # website (http://www.fmod.org/index.php/download).
  70. #
  71. # You can access the FMod module also via script for more options, such as
  72. # setting loop points (FMod.bgm_set_loop_points(first, second) in
  73. # milliseconds) and getting current BGS position (FMod.bgs_position) to
  74. # name a few.
  75. #------------------------------------------------------------------------------
  76. # Version Info:
  77. #   - Version 1.5:
  78. #       - Made the Volume and Pitch paramters to Audio ME/SE playing
  79. #         methods optional. (Thanks panchokoster!)
  80. #       - Script now uses FMOD's software mixer instead of hardware
  81. #         acceleration. This solves issues with certain sound cards.
  82. #       - A message is now displayed when a file isn't found, instead
  83. #         of just throwing the error number.
  84. #       - Added an independent thread to handle updating Audio module,
  85. #         instead of calling it in $game_system#update. This should
  86. #         ensure that it's called in all scenes. (Thanks Zeriab!)
  87. #       - Updated fading calculations to depend on seconds instead
  88. #         of game frames.
  89. #   - Version 1.4:
  90. #       - Fixed a bug where file isn't found if RTP path doesn't end
  91. #         with a backslash (thanks Atoa!).
  92. #       - Added BGM fading in after a ME is done playing to mimic
  93. #         original Audio class behavior, the volume increment when
  94. #         fading is specified by constant Audio::BGM_FADE_IN_INCREMENT.
  95. #       - Several minor bug fixes and minor behavior changes relating
  96. #         to fading out and stopping sounds.
  97. #   - Version 1.3:
  98. #       - Fixed a bug with ME fading out.
  99. #       - Added methods to get BGM and BGS length.
  100. #       - Providing -1 as loop end point to set_loop_points methods
  101. #         makes the sound file's end the loop end point.
  102. #       - Changed implementation of set_loop_points a bit.
  103. #       - Position of BGM/BGS to be played after fading is now
  104. #         remembered instead of starting all over.
  105. #   - Version 1.2:
  106. #       - Fully documented the script, and fixed some bugs.
  107. #       - Completely rewrote Audio module, allowing FMOD to handle
  108. #         BGMs, BGSs, MEs, and SEs except of just BGMs.
  109. #       - Fixed RTP reading to use registry instead of special files..
  110. #   - Version 1.1:
  111. #       - Added position tracking and adjusting.
  112. #       - Added loop point support.
  113. #       - Implemented BGM fading.
  114. #   - Version 1.0:
  115. #       - Hiretsukan (Kevin Gadd)'s initial release.
  116. #------------------------------------------------------------------------------
  117. # Known bugs:
  118. #
  119. #   - MIDI abrupt start when seeking or restoring from position
  120. #   - Found a bug or have some ideas for the next version? Please tell me!
  121. #------------------------------------------------------------------------------
  122. # Terms of Use:
  123. #
  124. #   Use of this script is subject to the permissive BSD-like license below.
  125. #   That basically means you could use it in any way you like as long
  126. #   as you keep the following copyright and license unchanged and available,
  127. #   and don't use name of copyright holder to promote products based on
  128. #   this software. Note, however, that this license only applies to the
  129. #   script, and not to the FMOD library. For more information about FMOD
  130. #   licenses consult FMOD website: http://www.fmod.org/index.php/sales
  131. #   It's free for non-commercial use, and they provide several types
  132. #   of licenses for different types of developers.
  133. #
  134. # Copyright (c) 2005, Kevin Gadd
  135. # All rights reserved.
  136. #
  137. # Redistribution and use in source and binary forms, with or without
  138. # modification, are permitted provided that the following conditions are met:
  139. #     * Redistributions of source code must retain the above copyright
  140. #       notice, this list of conditions and the following disclaimer.
  141. #     * Redistributions in binary form must reproduce the above copyright
  142. #       notice, this list of conditions and the following disclaimer in the
  143. #       documentation and/or other materials provided with the distribution.
  144. #     * The name of the contributors may not be used to endorse or promote
  145. #       products derived from this software without specific prior written
  146. #       permission.
  147. #
  148. # THIS SOFTWARE IS PROVIDED BY Kevin Gadd ''AS IS'' AND ANY
  149. # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  150. # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  151. # DISCLAIMED. IN NO EVENT SHALL Kevin Gadd BE LIABLE FOR ANY
  152. # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  153. # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  154. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  155. # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  156. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  157. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  158. #==============================================================================
  159.  
  160. #Codename: apu
  161.  
  162. ($imported ||= {})[:mikb89_apu_fmod] = true
  163.  
  164. # License:
  165. # - You can ask me to include support for other scripts as long as these scripts
  166. #   use the $imported[script] = true;
  167. # - You can modify and even repost my scripts, after having received a response
  168. #   by me. For reposting it, anyway, you must have done heavy edit or porting,
  169. #   you can't do a post with the script as is;
  170. # - You can use my scripts for whatever you want, from free to open to
  171. #   commercial games. I'd appreciate by the way if you let me know about what
  172. #   you're doing;
  173. # - You must credit me, if you use this script or part of it.
  174.  
  175. #==============================================================================
  176. # ** FModEx
  177. #------------------------------------------------------------------------------
  178. #  FMOD Ex binding by Kevin Gadd (janus@luminance.org)
  179. #==============================================================================
  180.  
  181. module FModEx
  182.   #--------------------------------------------------------------------------
  183.   # * Constants
  184.   #--------------------------------------------------------------------------
  185.   # FMOD_INITFLAGS flags
  186.   FMOD_INIT_NORMAL = 0
  187.   # FMOD_RESULT flags
  188.   FMOD_OK = 0
  189.   FMOD_ERR_CHANNEL_STOLEN = 11
  190.   FMOD_ERR_FILE_NOT_FOUND = 23
  191.   FMOD_ERR_INVALID_HANDLE = 36
  192.   # FMOD_MODE flags
  193.   FMOD_DEFAULT = 0
  194.   FMOD_LOOP_OFF = 1
  195.   FMOD_LOOP_NORMAL = 2
  196.   FMOD_LOOP_BIDI = 4
  197.   FMOD_LOOP_BITMASK = 7
  198.   FMOD_2D = 8
  199.   FMOD_3D = 16
  200.   FMOD_HARDWARE = 32
  201.   FMOD_SOFTWARE = 64
  202.   FMOD_CREATESTREAM = 128
  203.   FMOD_CREATESAMPLE = 256
  204.   FMOD_OPENUSER = 512
  205.   FMOD_OPENMEMORY = 1024
  206.   FMOD_OPENRAW = 2048
  207.   FMOD_OPENONLY = 4096
  208.   FMOD_ACCURATETIME = 8192
  209.   FMOD_MPEGSEARCH = 16384
  210.   FMOD_NONBLOCKING = 32768
  211.   FMOD_UNIQUE = 65536
  212.   # The default mode that the script uses
  213.   FMOD_DEFAULT_SOFTWARWE = FMOD_LOOP_OFF | FMOD_2D | FMOD_SOFTWARE
  214.   # FMOD_CHANNELINDEX flags
  215.   FMOD_CHANNEL_FREE = -1
  216.   FMOD_CHANNEL_REUSE = -2
  217.   # FMOD_TIMEUNIT_flags
  218.   FMOD_TIMEUNIT_MS = 1
  219.   FMOD_TIMEUNIT_PCM = 2
  220.   # The default time unit the script uses
  221.   FMOD_DEFAULT_UNIT = FMOD_TIMEUNIT_MS
  222.   # Types supported by FMOD Ex
  223.   FMOD_FILE_TYPES = ['ogg', 'aac', 'wma', 'mp3', 'wav', 'it', 'xm', 'mod', 's3m', 'mid', 'midi']
  224.   #add
  225.   FMOD_DSP_TYPES = ["UNKNOWN", "MIXER", "OSCILLATOR", "LOWPASS", "ITLOWPASS",
  226.                     "HIGHPASS", "ECHO", "FLANGE", "DISTORTION", "NORMALIZE",
  227.                     "PARAMEQ", "PITCHSHIFT", "CHORUS", "VSTPLUGIN",
  228.                     "WINAMPPLUGIN", "ITECHO", "COMPRESSOR", "SFXREVERB",
  229.                     "LOWPASS_SIMPLE", "DELAY", "TREMOLO", "LADSPAPLUGIN",
  230.                     "HIGHPASS_SIMPLE"]
  231.  
  232.   #============================================================================
  233.   # ** DLL
  234.   #----------------------------------------------------------------------------
  235.   #  A class that manages importing functions from the DLL
  236.   #============================================================================
  237.  
  238.   class DLL
  239.     #--------------------------------------------------------------------------
  240.     # * Public Instance Variables
  241.     #--------------------------------------------------------------------------
  242.     attr_accessor :filename           # DLL file name for instance    
  243.     attr_accessor :functions          # hash of functions imported (by name)
  244.     #--------------------------------------------------------------------------
  245.     # * Object Initialization
  246.     #     filename  : Name of the DLL
  247.     #--------------------------------------------------------------------------
  248.     def initialize(filename = 'fmodex.dll')
  249.       @@filename = filename
  250.       @@functions = {}
  251.       @handle = 0            # Handle to the DLL
  252.       # Load specified library into the address space of game process
  253.       w32_LL = Win32API.new('kernel32.dll', 'LoadLibrary', 'p', 'l')
  254.       @handle = w32_LL.call(filename)
  255.       # System functions:
  256.       self.import('System_Create', 'p')
  257.       self.import('System_Init', 'llll')
  258.       self.import('System_Close', 'l')
  259.       self.import('System_Release', 'l')
  260.       self.import('System_CreateSound', 'lpllp')
  261.       self.import('System_CreateStream', 'lpllp')
  262.       self.import('System_PlaySound', 'llllp')
  263.       #add
  264.       self.import('System_CreateDSPByType', 'llp')
  265.       # Sound functions:
  266.       self.import('Sound_Release', 'l')
  267.       self.import('Sound_GetMode', 'lp')
  268.       self.import('Sound_SetMode', 'll')
  269.       self.import('Sound_SetLoopPoints', 'lllll')
  270.       self.import('Sound_GetLength', 'lpl')
  271.       # Channel functions:
  272.       self.import('Channel_Stop', 'l')
  273.       self.import('Channel_IsPlaying', 'lp')
  274.       self.import('Channel_GetPaused', 'lp')
  275.       self.import('Channel_SetPaused', 'll')
  276.       self.import('Channel_GetVolume', 'lp')
  277.       self.import('Channel_SetVolume', 'll')
  278.       self.import('Channel_GetPan', 'lp')
  279.       self.import('Channel_SetPan', 'll')
  280.       self.import('Channel_GetFrequency', 'lp')
  281.       self.import('Channel_SetFrequency', 'll')
  282.       self.import('Channel_GetPosition', 'lpl')
  283.       self.import('Channel_SetPosition', 'lll')
  284.       #add
  285.       self.import('Channel_SetMute', 'll')
  286.       self.import('Channel_AddDSP', 'llp')
  287. #~       # Channel group functions:
  288. #~       self.import('ChannelGroup_SetMute', 'll')
  289.       #add
  290.       # DSP functions:
  291.       self.import('DSP_SetParameter', 'lll')
  292.       self.import('DSP_Release', 'l')
  293.       self.import('DSP_Remove', 'l')
  294.       self.import('DSP_GetNumParameters', 'lp')
  295.       self.import('DSP_GetParameter', 'llppl')
  296.       self.import('DSP_GetType', 'lp')
  297.     end
  298.     #--------------------------------------------------------------------------
  299.     # * Create a Win32API Object And Add it to Hashtable
  300.     #     name      : Function name
  301.     #     args      : Argument types (p = pointer, l = int, v = void)
  302.     #     returnType: Type of value returned by function
  303.     #--------------------------------------------------------------------------
  304.     def import(name, args = '', returnType = 'l')
  305.       @@functions[name] = Win32API.new(@@filename, 'FMOD_' + name, args, returnType)
  306.     end
  307.     #--------------------------------------------------------------------------
  308.     # * Get Function by Name
  309.     #     key       : Function name
  310.     #--------------------------------------------------------------------------
  311.     def [](key)
  312.       return @@functions[key]
  313.     end
  314.     #--------------------------------------------------------------------------
  315.     # * Call a Function With Passed Arguments
  316.     #     name      : Function name
  317.     #     args      : Argument to function
  318.     #--------------------------------------------------------------------------
  319.     def invoke(name, *args)
  320.       fn = @@functions[name]
  321.       raise "function not imported: #{name}" if fn.nil?
  322.       result = fn.call(*args)
  323.       unless result == FMOD_OK or result == FMOD_ERR_CHANNEL_STOLEN or
  324.         result == FMOD_ERR_FILE_NOT_FOUND
  325.         raise "FMOD Ex returned error #{result}"
  326.       end
  327.       return result
  328.     end
  329.     #--------------------------------------------------------------------------
  330.     # * Store Float as Binary Int Because Floats Can't be Passed Directly
  331.     #     f         : Float to convert
  332.     #--------------------------------------------------------------------------
  333.     def convertFloat(f)
  334.       # First pack the float in a string as a native binary float
  335.       temp = [f].pack('f')
  336.       # Then unpack the native binary float as an integer
  337.       return unpackInt(temp)
  338.     end
  339.     #--------------------------------------------------------------------------
  340.     # * Unpack Binary Data to Integer
  341.     #     s         : String containing binary data
  342.     #--------------------------------------------------------------------------
  343.     def unpackInt(s)
  344.       return s.unpack('l')[0]
  345.     end
  346.     #--------------------------------------------------------------------------
  347.     # * Unpack Binary Data to Float
  348.     #     s         : String containing binary data
  349.     #--------------------------------------------------------------------------
  350.     def unpackFloat(s)
  351.       return s.unpack('f')[0]
  352.     end
  353.     #--------------------------------------------------------------------------
  354.     # * Unpack Binary Data to Boolean
  355.     #     s         : String containing binary data
  356.     #--------------------------------------------------------------------------
  357.     def unpackBool(s)
  358.       return s.unpack('l')[0] != 0
  359.     end
  360.   end
  361.  
  362.   #============================================================================
  363.   # ** System
  364.   #----------------------------------------------------------------------------
  365.   #  A class that manages an instance of FMOD::System
  366.   #============================================================================
  367.  
  368.   class System
  369.     #--------------------------------------------------------------------------
  370.     # * Public Instance Variables
  371.     #--------------------------------------------------------------------------
  372.     attr_accessor :fmod               # Instance of DLL class (fmodex.dll)
  373.     attr_accessor :handle             # Handle (pointer) to System object
  374.     attr_accessor :maxChannels        # Maximum number of channels
  375.     #--------------------------------------------------------------------------
  376.     # * Object Initialization
  377.     #     fmod            : An instance of DLL class
  378.     #     maxChannels     : Maximum number of used channels
  379.     #     flags           : FMOD_INITFLAGS
  380.     #     extraDriverData : Driver specific data
  381.     #--------------------------------------------------------------------------
  382.     def initialize(theDLL, maxChannels = 32, flags = FMOD_INIT_NORMAL, extraDriverData = 0)
  383.       @fmod = theDLL
  384.       @maxChannels = maxChannels
  385.       # Create and initialize FMOD::System
  386.       temp = 0.chr * 4
  387.       @fmod.invoke('System_Create', temp)
  388.       @handle = @fmod.unpackInt(temp)
  389.       @fmod.invoke('System_Init', @handle, maxChannels, flags, extraDriverData)
  390.     end
  391.     #--------------------------------------------------------------------------
  392.     # * Create FMOD::DSP (fully loaded into memory by default)
  393.     #     type            : Type of the DSP
  394.     #--------------------------------------------------------------------------
  395.     def createDSPByType(type)
  396.       temp = 0.chr * 4
  397.       @fmod.invoke('System_CreateDSPByType', @handle, type, temp)
  398.       newDSP = DSP.new(self, @fmod.unpackInt(temp))
  399.       return newDSP
  400.     end
  401.     #--------------------------------------------------------------------------
  402.     # * Create FMOD::Sound (fully loaded into memory by default)
  403.     #     filename        : Name of file to open
  404.     #     mode            : FMOD_MODE flags
  405.     #--------------------------------------------------------------------------
  406.     def createSound(filename, mode = FMOD_DEFAULT_SOFTWARWE)
  407.       # Create sound and return it
  408.       temp = 0.chr * 4
  409.       result = @fmod.invoke('System_CreateSound', @handle, filename, mode, 0, temp)
  410.       raise "File not found: \"#{filename}\"" if result == FMOD_ERR_FILE_NOT_FOUND
  411.       newSound = Sound.new(self, @fmod.unpackInt(temp))
  412.       return newSound
  413.     end
  414.     #--------------------------------------------------------------------------
  415.     # * Create Streamed FMOD::Sound (chunks loaded on demand)
  416.     #     filename        : Name of file to open
  417.     #     mode            : FMOD_MODE flags
  418.     #--------------------------------------------------------------------------
  419.     def createStream(filename, mode = FMOD_DEFAULT_SOFTWARWE)
  420.       # Create sound and return it
  421.       temp = 0.chr * 4
  422.       result = @fmod.invoke('System_CreateStream', @handle, filename, mode, 0, temp)
  423.       raise "File not found: \"#{filename}\"" if result == FMOD_ERR_FILE_NOT_FOUND
  424.       newSound = Sound.new(self, @fmod.unpackInt(temp))
  425.       return newSound
  426.     end
  427.     #--------------------------------------------------------------------------
  428.     # * Close And Release System
  429.     #--------------------------------------------------------------------------
  430.     def dispose
  431.       if (@handle > 0)
  432.         @fmod.invoke('System_Close', @handle)
  433.         @fmod.invoke('System_Release', @handle)
  434.         @handle = 0
  435.       end
  436.       @fmod = nil
  437.     end
  438.   end
  439.  
  440.   #============================================================================
  441.   # ** Sound
  442.   #----------------------------------------------------------------------------
  443.   #  A class that manages an instance of FMOD::Sound
  444.   #============================================================================
  445.  
  446.   class Sound
  447.     #--------------------------------------------------------------------------
  448.     # * Public Instance Variables
  449.     #--------------------------------------------------------------------------
  450.     attr_accessor :system             # System that created this Sound
  451.     attr_accessor :fmod               # Instance of DLL class (fmodex.dll)
  452.     attr_accessor :handle             # Handle (pointer) to Sound object
  453.     #--------------------------------------------------------------------------
  454.     # * Object Initialization
  455.     #     theSystem       : The System that created this Sound object
  456.     #     handle          : Handle to the FMOD::Sound object
  457.     #--------------------------------------------------------------------------
  458.     def initialize(theSystem, theHandle)
  459.       @system = theSystem
  460.       @fmod = theSystem.fmod
  461.       @handle = theHandle
  462.     end
  463.     #--------------------------------------------------------------------------
  464.     # * Play Sound
  465.     #     paused          : Start paused?
  466.     #     channel         : Channel allocated to sound (nil for automatic)
  467.     #--------------------------------------------------------------------------
  468.     def play(paused = false, channel = nil)
  469.       # If channel wasn't specified, let FMOD pick a free one,
  470.       # otherwise use the passed channel (id from 0 to maxChannels)
  471.       unless channel
  472.         temp = 0.chr * 4
  473.       else
  474.         temp = [channel].pack('l')
  475.       end
  476.       @fmod.invoke('System_PlaySound', @system.handle,
  477.                 (channel == nil) ? FMOD_CHANNEL_FREE : FMOD_CHANNEL_REUSE,
  478.                 @handle,
  479.                 (paused == true) ? 1 : 0,
  480.                 temp)
  481.       theChannel = @fmod.unpackInt(temp)
  482.       # Create a Channel object based on returned channel
  483.       newChannel = Channel.new(self, theChannel)
  484.       return newChannel
  485.     end
  486.     #--------------------------------------------------------------------------
  487.     # * Get FMOD_MODE Bits
  488.     #--------------------------------------------------------------------------
  489.     def mode
  490.       temp = 0.chr * 4
  491.       @fmod.invoke('Sound_GetMode', @handle, temp)
  492.       return @fmod.unpackInt(temp)
  493.     end
  494.     #--------------------------------------------------------------------------
  495.     # * Set FMOD_MODE Bits
  496.     #--------------------------------------------------------------------------
  497.     def mode=(newMode)
  498.       @fmod.invoke('Sound_SetMode', @handle, newMode)
  499.     end
  500.     #--------------------------------------------------------------------------
  501.     # * Get FMOD_LOOP_MODE
  502.     #--------------------------------------------------------------------------  
  503.     def loopMode
  504.       temp = 0.chr * 4
  505.       @fmod.invoke('Sound_GetMode', @handle, temp)
  506.       return @fmod.unpackInt(temp) & FMOD_LOOP_BITMASK
  507.     end
  508.     #--------------------------------------------------------------------------
  509.     # * Set FMOD_LOOP_MODE
  510.     #--------------------------------------------------------------------------  
  511.     def loopMode=(newMode)
  512.       @fmod.invoke('Sound_SetMode', @handle, (self.mode & ~FMOD_LOOP_BITMASK) | newMode)
  513.     end
  514.     #--------------------------------------------------------------------------
  515.     # * Return Sound Length
  516.     #--------------------------------------------------------------------------
  517.     def length(unit = FMOD_DEFAULT_UNIT)
  518.       temp = 0.chr * 4
  519.       @fmod.invoke('Sound_GetLength', @handle, temp, unit)
  520.       return @fmod.unpackInt(temp)
  521.     end
  522.     #--------------------------------------------------------------------------
  523.     # * Set Loop Points
  524.     #     first           : Loop start point in milliseconds
  525.     #     second          : Loop end point in milliseconds
  526.     #     unit            : FMOD_TIMEUNIT for points
  527.     #--------------------------------------------------------------------------    
  528.     def setLoopPoints(first, second, unit = FMOD_DEFAULT_UNIT)
  529.       @fmod.invoke('Sound_SetLoopPoints', @handle, first, unit, second, unit)
  530.     end
  531.     #--------------------------------------------------------------------------
  532.     # * Release Sound
  533.     #--------------------------------------------------------------------------
  534.     def dispose
  535.       if (@handle > 0)
  536.         @fmod.invoke('Sound_Release', @handle)
  537.         @handle = 0
  538.       end
  539.       @fmod = nil
  540.       @system = nil
  541.     end
  542.   end
  543.  
  544.   #============================================================================
  545.   # ** DSP (add)
  546.   #----------------------------------------------------------------------------
  547.   #  A class that manages an instance of FMOD::DSP
  548.   #============================================================================
  549.  
  550.   class DSP
  551.     #--------------------------------------------------------------------------
  552.     # * Public Instance Variables
  553.     #--------------------------------------------------------------------------
  554.     attr_accessor :system             # System that created this DSP
  555.     attr_accessor :fmod               # Instance of DLL class (fmodex.dll)
  556.     attr_accessor :handle             # Handle (pointer) to DSP object
  557.     #--------------------------------------------------------------------------
  558.     # * Object Initialization
  559.     #     theSystem       : The System that created this DSP object
  560.     #     handle          : Handle to the FMOD::DSP object
  561.     #--------------------------------------------------------------------------
  562.     def initialize(theSystem, theHandle)
  563.       @system = theSystem
  564.       @fmod = theSystem.fmod
  565.       @handle = theHandle
  566.     end
  567.     #--------------------------------------------------------------------------
  568.     # * Get the type of this DSP effect (0 -> FMOD_DSP_TYPE.size - 1)
  569.     #--------------------------------------------------------------------------
  570.     def getType
  571.       temp = 0.chr * 4
  572.       @fmod.invoke('DSP_GetType', @handle, temp) if (@handle > 0)
  573.       return @fmod.unpackInt(temp)
  574.     end
  575.     #--------------------------------------------------------------------------
  576.     # * Get the name of this DSP effect type
  577.     #--------------------------------------------------------------------------
  578.     def getTypeName
  579.       return FModEx::FMOD_DSP_TYPES[getType]
  580.     end
  581.     #--------------------------------------------------------------------------
  582.     # * Get the positive integer number of parameters this DSP type supports
  583.     #--------------------------------------------------------------------------
  584.     def getNumParameters
  585.       temp = 0.chr * 4
  586.       @fmod.invoke('DSP_GetNumParameters', @handle, temp) if (@handle > 0)
  587.       return @fmod.unpackInt(temp)
  588.     end
  589.     #--------------------------------------------------------------------------
  590.     # * Get the float value of the specified parameter
  591.     #--------------------------------------------------------------------------
  592.     def getParameter(which = 0)
  593.       temp = 0.chr * 4
  594.       tempstr = 0.chr * 256
  595.       value = -1
  596.       @fmod.invoke('DSP_GetParameter', @handle, which, temp, tempstr, value) if (@handle > 0)
  597.       return @fmod.unpackFloat(temp)
  598.     end
  599.     #--------------------------------------------------------------------------
  600.     # * Set the value for a parameter
  601.     #     param           : The ID of the parameter
  602.     #     value           : The float value to set
  603.     #--------------------------------------------------------------------------
  604.     def setParameter(param, value)
  605.       if param >= 0
  606.         @fmod.invoke('DSP_SetParameter', @handle, param, @fmod.convertFloat(value)) if (@handle > 0)
  607.       end
  608.     end
  609.     #--------------------------------------------------------------------------
  610.     # * Remove DSP effect
  611.     #--------------------------------------------------------------------------
  612.     def remove
  613.       if (@handle > 0)
  614.         @fmod.invoke('DSP_Remove', @handle)
  615.         dispose
  616.       end
  617.     end
  618.     #--------------------------------------------------------------------------
  619.     # * Release DSP
  620.     #--------------------------------------------------------------------------
  621.     def dispose
  622.       if (@handle > 0)
  623.         @fmod.invoke('DSP_Release', @handle)
  624.         @handle = 0
  625.       end
  626.       @fmod = nil
  627.       @system = nil
  628.     end
  629.   end
  630.  
  631.   #============================================================================
  632.   # ** Channel
  633.   #----------------------------------------------------------------------------
  634.   #  A class that represents an FMOD::Channel
  635.   #============================================================================
  636.  
  637.   class Channel
  638.     #--------------------------------------------------------------------------
  639.     # * Public Instance Variables
  640.     #--------------------------------------------------------------------------
  641.     attr_accessor :system             # System that created the Sound
  642.     attr_accessor :sound              # Sound using the Channel
  643.     attr_accessor :fmod               # Instance of DLL class (fmodex.dll)
  644.     attr_accessor :handle             # Handle (pointer) to Sound object
  645.     attr_accessor :dsps               # Array of DSP effects
  646.     #--------------------------------------------------------------------------
  647.     # * Object Initialization
  648.     #     theSound        : The Sound using this Channel object
  649.     #     handle          : Handle to the FMOD::Channel object
  650.     #--------------------------------------------------------------------------
  651.     def initialize(theSound, theHandle)
  652.       @sound = theSound
  653.       @system = theSound.system
  654.       @fmod = theSound.system.fmod
  655.       @handle = theHandle
  656.       @dsps = []
  657.     end
  658.     #--------------------------------------------------------------------------
  659.     # * Stop Channel and Make it Available for Other Sounds
  660.     #--------------------------------------------------------------------------
  661.     def stop
  662.       @fmod.invoke('Channel_Stop', @handle)
  663.     end
  664.     #--------------------------------------------------------------------------
  665.     # * Is the Channel Handle Valid?
  666.     #--------------------------------------------------------------------------
  667.     def valid?
  668.       temp = 0.chr * 4
  669.       begin
  670.         result = @fmod.invoke('Channel_IsPlaying', @handle, temp)
  671.       rescue
  672.         if (result == FMOD_ERR_INVALID_HANDLE)
  673.           return false
  674.         else
  675.           raise
  676.         end
  677.       end
  678.       # If we get here then it's valid
  679.       return true
  680.     end
  681.     #--------------------------------------------------------------------------
  682.     # * Is the Channel Playing?
  683.     #--------------------------------------------------------------------------
  684.     def playing?
  685.       temp = 0.chr * 4
  686.       @fmod.invoke('Channel_IsPlaying', @handle, temp)
  687.       return @fmod.unpackBool(temp)
  688.     end
  689.     #--------------------------------------------------------------------------
  690.     # * Get Channel Volume Level (0.0 -> 1.0)
  691.     #--------------------------------------------------------------------------
  692.     def volume
  693.       temp = 0.chr * 4
  694.       @fmod.invoke('Channel_GetVolume', @handle, temp)
  695.       return @fmod.unpackFloat(temp)
  696.     end
  697.     #--------------------------------------------------------------------------
  698.     # * Set Channel Volume Level (0.0 -> 1.0)
  699.     #--------------------------------------------------------------------------
  700.     def volume=(newVolume)
  701.       @fmod.invoke('Channel_SetVolume', @handle, @fmod.convertFloat(newVolume))
  702.     end
  703.     #--------------------------------------------------------------------------
  704.     # * Set Channel Mute
  705.     #--------------------------------------------------------------------------
  706.     def mute
  707.       @fmod.invoke('Channel_SetMute', @handle, 1)
  708.     end
  709.     #--------------------------------------------------------------------------
  710.     # * Set Channel Unmute
  711.     #--------------------------------------------------------------------------
  712.     def unmute
  713.       @fmod.invoke('Channel_SetMute', @handle, 0)
  714.     end
  715.     #--------------------------------------------------------------------------
  716.     # * Get Channel Pan Position (-1.0 -> 1.0)
  717.     #--------------------------------------------------------------------------
  718.     def pan
  719.       temp = 0.chr * 4
  720.       @fmod.invoke('Channel_GetPan', @handle, temp)
  721.       return @fmod.unpackFloat(temp)
  722.     end
  723.     #--------------------------------------------------------------------------
  724.     # * Set Channel Pan Position (-1.0 -> 1.0)
  725.     #--------------------------------------------------------------------------
  726.     def pan=(newPan)
  727.       @fmod.invoke('Channel_SetPan', @handle, @fmod.convertFloat(newPan))
  728.     end
  729.     #--------------------------------------------------------------------------
  730.     # * Get Channel Frequency in HZ (Speed/Pitch)
  731.     #--------------------------------------------------------------------------
  732.     def frequency
  733.       temp = 0.chr * 4
  734.       @fmod.invoke('Channel_GetFrequency', @handle, temp)
  735.       return @fmod.unpackFloat(temp)
  736.     end
  737.     #--------------------------------------------------------------------------
  738.     # * Set Channel Frequency in HZ (Speed/Pitch)
  739.     #--------------------------------------------------------------------------
  740.     def frequency=(newFrequency)
  741.       @fmod.invoke('Channel_SetFrequency', @handle, @fmod.convertFloat(newFrequency))
  742.     end
  743.     #--------------------------------------------------------------------------
  744.     # * Is Channel Paused?
  745.     #--------------------------------------------------------------------------
  746.     def paused
  747.       temp = 0.chr * 4
  748.       @fmod.invoke('Channel_GetPaused', @handle, temp)
  749.       return @fmod.unpackBool(temp)
  750.     end
  751.     #--------------------------------------------------------------------------
  752.     # * Pause Channel
  753.     #--------------------------------------------------------------------------
  754.     def paused=(newPaused)
  755.       @fmod.invoke('Channel_SetPaused', @handle, (newPaused == true) ? 1 : 0)
  756.     end
  757.     #--------------------------------------------------------------------------
  758.     # * Get Current Playback Position
  759.     #     unit            : FMOD_TIMEUNIT to return position in
  760.     #--------------------------------------------------------------------------  
  761.     def position(unit = FMOD_DEFAULT_UNIT)
  762.       temp = 0.chr * 4
  763.       @fmod.invoke('Channel_GetPosition', @handle, temp, unit)
  764.       return @fmod.unpackInt(temp)
  765.     end
  766.     #--------------------------------------------------------------------------
  767.     # * Set Current Playback Position
  768.     #     newPosition     : New playback position
  769.     #     unit            : FMOD_TIMEUNIT to use when setting position
  770.     #--------------------------------------------------------------------------    
  771.     def position=(newPosition, unit = FMOD_DEFAULT_UNIT)
  772.       @fmod.invoke('Channel_SetPosition', @handle, newPosition, unit)
  773.     end
  774.     #--------------------------------------------------------------------------
  775.     # * Add a DSP by type
  776.     #     type            : Type of DSP effect
  777.     #--------------------------------------------------------------------------    
  778.     def addDSP(type)
  779.       dsp = @system.createDSPByType(type)
  780.       @fmod.invoke('Channel_AddDSP', @handle, dsp.handle, nil)
  781.       @dsps << dsp
  782.       return @dsps.size - 1
  783.     end
  784.     #--------------------------------------------------------------------------
  785.     # * Remove a DSP
  786.     #     id              : The index of the DSP effect
  787.     #--------------------------------------------------------------------------    
  788.     def removeDSP(id)
  789.       return false if @dsps.size <= id
  790.       dsp = @dsps[id]
  791.       dsp.remove
  792.       @dsps.delete_at(id)
  793.       return @dsps.compact! != nil
  794.     end
  795.     #--------------------------------------------------------------------------
  796.     # * Get a DSP for settings
  797.     #     id              : The index of the DSP effect
  798.     #--------------------------------------------------------------------------  
  799.     def DSP(id)
  800.       return @dsps[id]
  801.     end
  802.     #--------------------------------------------------------------------------
  803.     # * Dispose of Channel
  804.     #--------------------------------------------------------------------------  
  805.     def dispose
  806.       for dsp in @dsps
  807.         dsp.dispose
  808.       end
  809.       @handle = 0
  810.       @sound = nil
  811.       @system = nil
  812.       @fmod = nil
  813.     end
  814.   end
  815.  
  816. end
  817.  
  818. #==============================================================================
  819. # ** FMod
  820. #------------------------------------------------------------------------------
  821. #  A higher level module to access FMOD Ex
  822. #==============================================================================
  823.  
  824. module FMod
  825.  
  826.   #============================================================================
  827.   # ** SoundFile
  828.   #----------------------------------------------------------------------------
  829.   #  Represents a Sound file (BGM, BGS, SE, etc.) and associated Channel
  830.   #============================================================================
  831.  
  832.   class SoundFile
  833.     #--------------------------------------------------------------------------
  834.     # * Public Instance Variables
  835.     #--------------------------------------------------------------------------
  836.     attr_accessor :name                     # File name
  837.     attr_accessor :sound                    # FModEx::Sound object
  838.     attr_accessor :channel                  # Channel playing sound
  839.     attr_accessor :volume                   # Volume in RPG::AudioFile format
  840.     attr_accessor :pitch                    # Pitch in RPG::AudioFile format
  841.     attr_accessor :looping                  # Sound loops
  842.     attr_accessor :streaming                # Sound is streamed
  843.     attr_accessor :length                   # Sound length in milliseconds
  844.     #--------------------------------------------------------------------------
  845.     # * Object Initialization
  846.     #--------------------------------------------------------------------------
  847.     def initialize(name, sound, channel, volume, pitch, looping, streaming, length)
  848.       @name = name
  849.       @sound = sound
  850.       @channel = channel
  851.       @volume = volume
  852.       @pitch = pitch
  853.       @looping = looping
  854.       @streaming = streaming
  855.       @length = length
  856.     end
  857.   end
  858.   #--------------------------------------------------------------------------
  859.   # * Instance Variables
  860.   #--------------------------------------------------------------------------
  861.   @fmod_dll = FModEx::DLL.new               # The FMOD Ex DLL
  862.   @fmod = FModEx::System.new(@fmod_dll)     # The global System object
  863.   @@fmod_bgm = []                             # Array of Sound Effects
  864.   @@fmod_bgs = []                             # Array of Sound Effects
  865.   @@fmod_me = []                             # Array of Sound Effects
  866.   @@fmod_se = []                             # Array of Sound Effects
  867.   @@rtp_folder = nil                         # Name of RTP folder
  868.   #--------------------------------------------------------------------------
  869.   # * Get Path of RTP Folder From Registry
  870.   #--------------------------------------------------------------------------
  871.   def self.getRTPFolder
  872.     return @@rtp_folder if @@rtp_folder
  873.     open_key = Win32API.new('advapi32.dll', 'RegOpenKeyExA', 'LPLLP', 'L')
  874.     query_value = Win32API.new('advapi32.dll', 'RegQueryValueExA', 'LPLPPP', 'L')
  875.     close_key = Win32API.new('advapi32', 'RegCloseKey', 'L', 'L')
  876.     key = 0.chr * 4
  877.     # Open a HKEY_LOCAL_MACHINE with KEY_READ attribute and save handle in key
  878.     open_key.call(0x80000002, 'Software\Enterbrain\RGSS2\RTP', 0, 0x20019, key)
  879.     key = @fmod_dll.unpackInt(key)
  880.     type = 0.chr * 4
  881.     size = 0.chr * 4
  882.     # Read RTP folder to use from setting in Game.ini
  883.     rtp = self.read_ini("RTP")
  884.     # Query to get string size
  885.     query_value.call(key, rtp, 0, type, 0, size)
  886.     data = ' ' * @fmod_dll.unpackInt(size)
  887.     # Query the string value itself using size
  888.     query_value.call(key, rtp, 0, type, data, size)
  889.     @@rtp_folder = data.chop
  890.     close_key.call(key)
  891.     # Convert the addres to the RGSS standard
  892.     @@rtp_folder.gsub!(/\\/, "/")
  893.     @@rtp_folder += "/" if @@rtp_folder[-1] != "/"
  894.     return @@rtp_folder
  895.   end
  896.   #--------------------------------------------------------------------------
  897.   # * Read data from ini file
  898.   #     variable        : Data to read
  899.   #     filename        : Name without extension
  900.   #--------------------------------------------------------------------------
  901.   def self.read_ini(variable,filename="Game")
  902.     reg = /^#{variable}=(.*)$/
  903.     File.foreach(filename+'.ini') { |line| break($1) if line =~ reg }
  904.   end
  905.   #--------------------------------------------------------------------------
  906.   # * Return Proper File Name (With Extensions)
  907.   #     name            : Name of the file
  908.   #     extensions      : Extensions to add to file name
  909.   #--------------------------------------------------------------------------
  910.   def self.checkExtensions(name, extensions)
  911.     if FileTest.exist?(name)
  912.       return name
  913.     end
  914.     # Add extension if needed
  915.     extensions.each do |ext|
  916.       if FileTest.exist?(name + '.' + ext)
  917.         return name + '.' + ext
  918.       end
  919.     end
  920.     # File doesn't exist
  921.     return name
  922.   end
  923.   #--------------------------------------------------------------------------
  924.   # * Get Valid File Name
  925.   #     name            : Name of the file
  926.   #--------------------------------------------------------------------------
  927.   def self.selectBGMFilename(name, likeRTP = true)
  928.     return Dir.glob(name+'.*')[0] || Dir.glob(name)[0] ||
  929.            Dir.glob(self.getRTPFolder+name+'.*')[0] ||
  930.            Dir.glob(self.getRTPFolder+name)[0] if likeRTP
  931.     # See if file exists in game folder
  932.     localname = self.checkExtensions(name, FModEx::FMOD_FILE_TYPES)
  933.     if FileTest.exist?(localname)
  934.       return localname
  935.     end
  936.     # See if file exists in RTP
  937.     commonname = self.checkExtensions(self.getRTPFolder + name, FModEx::FMOD_FILE_TYPES)
  938.     if FileTest.exist?(commonname)
  939.       return commonname
  940.     end
  941.     # An invalid name was provided
  942.     raise name
  943.   end
  944.   #--------------------------------------------------------------------------
  945.   # * Play a Sound File Then Return it
  946.   #     name            : Name of the file
  947.   #     volume          : Channel volume
  948.   #     pitch           : Channel frequency
  949.   #     position        : Starting position in milliseconds
  950.   #     looping         : Does the sound loop?
  951.   #     streaming       : Stream sound or load whole thing to memory?
  952.   #--------------------------------------------------------------------------
  953.   def self.play(name, volume, pitch, position, looping, streaming)
  954.     # Get a valid file name
  955.     filename = self.selectBGMFilename(name)
  956.     # Create Sound or Stream and set initial values
  957.     sound = streaming ? @fmod.createStream(filename) : @fmod.createSound(filename)
  958.     sound.loopMode = looping ? FModEx::FMOD_LOOP_NORMAL : FModEx::FMOD_LOOP_OFF
  959.     channel = sound.play
  960.     volume = volume * 1.0
  961.     pitch = pitch * 1.0
  962.     file_length = sound.length(FModEx::FMOD_DEFAULT_UNIT)
  963.     sound_file = SoundFile.new(filename, sound, channel, volume,
  964.                                 pitch, looping, streaming, file_length)
  965.     sound_file.channel.volume = volume / 100.0
  966.     sound_file.channel.frequency = sound_file.channel.frequency * pitch / 100
  967.     sound_file.channel.position = position
  968.     self.play_operations(sound_file)
  969.     return sound_file
  970.   end
  971.   #--------------------------------------------------------------------------
  972.   # * Do something on new Sound File
  973.   #--------------------------------------------------------------------------
  974.   def self.play_operations(sound_file)
  975.     # alias this if you want to do something on the newly played files :)
  976.   end
  977.   #--------------------------------------------------------------------------
  978.   # * Replay of Sound File
  979.   #--------------------------------------------------------------------------
  980.   def self.replay(sound_file, volume = 100, pitch = 100, position = 0, reset = false)
  981.     self.set_volume(sound_file, volume)
  982.     self.set_pitch(sound_file, pitch)
  983.     self.set_position(sound_file, position) if position != 0 || (position == 0 && reset)
  984.   end
  985.   #--------------------------------------------------------------------------
  986.   # * Stop and Dispose of Sound File
  987.   #--------------------------------------------------------------------------
  988.   def self.stop(sound_file)
  989.     unless sound_file and sound_file.channel
  990.       return
  991.     end
  992.     # Stop channel, then clear variables and dispose of bgm
  993.     sound_file.channel.stop
  994.     sound_file.channel = nil
  995.     sound_file.sound.dispose
  996.   end
  997.   #--------------------------------------------------------------------------
  998.   # * Return Length in Milliseconds
  999.   #--------------------------------------------------------------------------
  1000.   def self.get_length(sound_file, unit = FModEx::FMOD_DEFAULT_UNIT)
  1001.     return sound_file.length(unit)
  1002.   end
  1003.   #--------------------------------------------------------------------------
  1004.   # * Check if Another Sound File is Playing
  1005.   #--------------------------------------------------------------------------
  1006.   def self.already_playing?(sound_file, name, position = 0)
  1007.     # Get a valid file name
  1008.     filename = self.selectBGMFilename(name)
  1009.     if (sound_file)
  1010.       # If the same sound file is already playing don't play it again
  1011.       if (sound_file.name == filename and position == 0)
  1012.         return true
  1013.       end
  1014.       # If another sound file is playing, stop it
  1015.       if sound_file.channel
  1016.         self.stop(sound_file)
  1017.       end
  1018.     end
  1019.     # No sound file is playing or it was already stopped
  1020.     return false
  1021.   end
  1022.   #--------------------------------------------------------------------------
  1023.   # * Check if this Sound File is Playing
  1024.   #--------------------------------------------------------------------------
  1025.   def self.playing_this?(sound_file, name)    
  1026.     if (sound_file)
  1027.       if (sound_file.name == name)
  1028.         return true
  1029.       end
  1030.     end
  1031.     return false
  1032.   end
  1033.   #--------------------------------------------------------------------------
  1034.   # * Check if Sound File is Playing
  1035.   #--------------------------------------------------------------------------  
  1036.   def self.playing?(sound_file)
  1037.     unless sound_file and sound_file.channel
  1038.       return false
  1039.     end
  1040.     return sound_file.channel.playing?
  1041.   end
  1042.   #--------------------------------------------------------------------------
  1043.   # * Get Current Sound File Playing Position
  1044.   #--------------------------------------------------------------------------
  1045.   def self.get_position(sound_file)
  1046.     unless sound_file and sound_file.channel
  1047.       return 0
  1048.     end
  1049.     return sound_file.channel.position
  1050.   end
  1051.   #--------------------------------------------------------------------------
  1052.   # * Seek to a New Sound File Playing Position
  1053.   #--------------------------------------------------------------------------
  1054.   def self.set_position(sound_file, new_pos)
  1055.     unless sound_file and sound_file.channel
  1056.       return
  1057.     end
  1058.     sound_file.channel.position = new_pos
  1059.   end
  1060.   #--------------------------------------------------------------------------
  1061.   # * Get Current Sound File Volume
  1062.   #--------------------------------------------------------------------------
  1063.   def self.get_volume(sound_file)
  1064.     unless sound_file
  1065.       return 0
  1066.     end
  1067.     return sound_file.volume
  1068.   end
  1069.   #--------------------------------------------------------------------------
  1070.   # * Set Sound File Volume
  1071.   #--------------------------------------------------------------------------
  1072.   def self.set_volume(sound_file, volume)
  1073.     unless sound_file and sound_file.channel
  1074.       return
  1075.     end
  1076.     sound_file.volume = volume * 1.0
  1077.     sound_file.channel.volume = volume / 100.0
  1078.   end
  1079.   #--------------------------------------------------------------------------
  1080.   # * Set Sound File Mute
  1081.   #--------------------------------------------------------------------------
  1082.   def self.set_mute(sound_file)
  1083.     unless sound_file and sound_file.channel
  1084.       return
  1085.     end
  1086.     sound_file.channel.mute
  1087.   end
  1088.   #--------------------------------------------------------------------------
  1089.   # * Set Sound File Unmute
  1090.   #--------------------------------------------------------------------------
  1091.   def self.set_unmute(sound_file)
  1092.     unless sound_file and sound_file.channel
  1093.       return
  1094.     end
  1095.     sound_file.channel.unmute
  1096.   end
  1097.   #--------------------------------------------------------------------------
  1098.   # * Get Current Sound File Pitch
  1099.   #--------------------------------------------------------------------------
  1100.   def self.get_pitch(sound_file)
  1101.     unless sound_file
  1102.       return 0
  1103.     end
  1104.     return sound_file.pitch
  1105.   end
  1106.   #--------------------------------------------------------------------------
  1107.   # * Set Sound File Pitch
  1108.   #--------------------------------------------------------------------------
  1109.   def self.set_pitch(sound_file, pitch)
  1110.     unless sound_file and sound_file.channel
  1111.       return
  1112.     end
  1113.     sound_file.pitch = pitch * 1.0
  1114.     sound_file.channel.frequency = sound_file.channel.frequency * pitch / 100.0
  1115.   end
  1116.   #--------------------------------------------------------------------------
  1117.   # * Get Current Sound File Pan
  1118.   #--------------------------------------------------------------------------
  1119.   def self.get_pan(sound_file)
  1120.     unless sound_file and sound_file.channel
  1121.       return 0
  1122.     end
  1123.     return sound_file.channel.pan
  1124.   end
  1125.   #--------------------------------------------------------------------------
  1126.   # * Set Sound File Pan
  1127.   #--------------------------------------------------------------------------
  1128.   def self.set_pan(sound_file, pan)
  1129.     unless sound_file and sound_file.channel
  1130.       return
  1131.     end
  1132.     sound_file.channel.pan = pan
  1133.   end
  1134.   #--------------------------------------------------------------------------
  1135.   # * Set Loop Points
  1136.   #     first           : Loop start point in milliseconds
  1137.   #     second          : Loop end point in milliseconds (-1 for file end)
  1138.   #     unit            : FMOD_TIMEUNIT for points
  1139.   #--------------------------------------------------------------------------
  1140.   def self.set_loop_points(sound_file, first, second, unit = FModEx::FMOD_DEFAULT_UNIT)
  1141.     unless sound_file and sound_file.channel
  1142.       return
  1143.     end
  1144.     # If second is -1 then set loop end to the file end
  1145.     if second == -1
  1146.       second = sound_file.length - 1
  1147.     end
  1148.     # Set loop points and reflush stream buffer
  1149.     sound_file.channel.sound.setLoopPoints(first, second, unit)
  1150.     sound_file.channel.position = sound_file.channel.position
  1151.     return sound_file
  1152.   end
  1153.   #--------------------------------------------------------------------------
  1154.   # * Play BGM (or ME)
  1155.   #     name            : Name of the file
  1156.   #     volume          : Channel volume
  1157.   #     pitch           : Channel frequency
  1158.   #     position        : Starting position in milliseconds
  1159.   #     looping         : Does the BGM loop?
  1160.   #--------------------------------------------------------------------------
  1161.   def self.bgm_play(name, volume, pitch, c, position = 0, looping = true)
  1162.     al_pl = self.already_playing?(@@fmod_bgm[c], name, position)
  1163.     self.bgm_set_volume(volume, c) if al_pl
  1164.     return if al_pl
  1165.     # Now play the new BGM as a stream
  1166.     @@fmod_bgm[c] = self.play(name, volume, pitch, position, looping, true)
  1167.   end
  1168.   #--------------------------------------------------------------------------
  1169.   # * Stop and Dispose of BGM
  1170.   #--------------------------------------------------------------------------
  1171.   def self.bgm_stop(c)
  1172.     self.stop(@@fmod_bgm[c])
  1173.     @@fmod_bgm[c] = nil
  1174.   end
  1175.   #--------------------------------------------------------------------------
  1176.   # * Return BGM Length in Milliseconds
  1177.   #--------------------------------------------------------------------------
  1178.   def self.bgm_length(c)
  1179.     self.get_length(@@fmod_bgm[c])
  1180.   end
  1181.   #--------------------------------------------------------------------------
  1182.   # * Check if a BGM is Playing
  1183.   #--------------------------------------------------------------------------  
  1184.   def self.bgm_playing?(c)
  1185.     return self.playing?(@@fmod_bgm[c])
  1186.   end
  1187.   #--------------------------------------------------------------------------
  1188.   # * Get Current BGM Playing Position
  1189.   #--------------------------------------------------------------------------
  1190.   def self.bgm_position(c)
  1191.     return self.get_position(@@fmod_bgm[c])
  1192.   end
  1193.   #--------------------------------------------------------------------------
  1194.   # * Seek to New BGM Playing Position
  1195.   #--------------------------------------------------------------------------
  1196.   def self.bgm_set_position(new_pos, c)
  1197.     self.set_position(@@fmod_bgm[c], new_pos)
  1198.   end
  1199.   #--------------------------------------------------------------------------
  1200.   # * Get Current BGM Volume
  1201.   #--------------------------------------------------------------------------
  1202.   def self.bgm_volume(c)
  1203.     return self.get_volume(@@fmod_bgm[c])
  1204.   end
  1205.   #--------------------------------------------------------------------------
  1206.   # * Set BGM Volume
  1207.   #--------------------------------------------------------------------------
  1208.   def self.bgm_set_volume(volume, c)
  1209.     self.set_volume(@@fmod_bgm[c], volume)
  1210.   end
  1211.   #--------------------------------------------------------------------------
  1212.   # * Set BGM Mute
  1213.   #--------------------------------------------------------------------------
  1214.   def self.bgm_set_mute(c)
  1215.     self.set_mute(@@fmod_bgm[c])
  1216.   end
  1217.   #--------------------------------------------------------------------------
  1218.   # * Set BGM Unmute
  1219.   #--------------------------------------------------------------------------
  1220.   def self.bgm_set_unmute(c)
  1221.     self.set_unmute(@@fmod_bgm[c])
  1222.   end
  1223.   #--------------------------------------------------------------------------
  1224.   # * Get Current BGM Pan
  1225.   #--------------------------------------------------------------------------
  1226.   def self.bgm_pan(c)
  1227.     return self.get_pan(@@fmod_bgm[c])
  1228.   end
  1229.   #--------------------------------------------------------------------------
  1230.   # * Set BGM Pan
  1231.   #--------------------------------------------------------------------------
  1232.   def self.bgm_set_pan(pan, c)
  1233.     self.set_pan(@@fmod_bgm[c], pan)
  1234.   end
  1235.   #--------------------------------------------------------------------------
  1236.   # * Set ME Volume
  1237.   #--------------------------------------------------------------------------
  1238.   def self.me_set_volume(volume, c)
  1239.     self.set_volume(@@fmod_me[c], volume)
  1240.   end
  1241.   #--------------------------------------------------------------------------
  1242.   # * Set ME Mute
  1243.   #--------------------------------------------------------------------------
  1244.   def self.me_set_mute(c)
  1245.     self.set_mute(@@fmod_me[c])
  1246.   end
  1247.   #--------------------------------------------------------------------------
  1248.   # * Set ME Unmute
  1249.   #--------------------------------------------------------------------------
  1250.   def self.me_set_unmute(c)
  1251.     self.set_unmute(@@fmod_me[c])
  1252.   end
  1253.   #--------------------------------------------------------------------------
  1254.   # * Get Current ME Pan
  1255.   #--------------------------------------------------------------------------
  1256.   def self.me_pan(c)
  1257.     return self.get_pan(@@fmod_me[c])
  1258.   end
  1259.   #--------------------------------------------------------------------------
  1260.   # * Set ME Pan
  1261.   #--------------------------------------------------------------------------
  1262.   def self.me_set_pan(pan, c)
  1263.     self.set_pan(@@fmod_me[c], pan)
  1264.   end
  1265.   #--------------------------------------------------------------------------
  1266.   # * Set Loop Points
  1267.   #     first           : Loop start point in milliseconds
  1268.   #     second          : Loop end point in milliseconds
  1269.   #     unit            : FMOD_TIMEUNIT for points
  1270.   #--------------------------------------------------------------------------
  1271.   def self.bgm_set_loop_points(first, second, c, unit = FModEx::FMOD_DEFAULT_UNIT)
  1272.     @@fmod_bgm[c] = self.set_loop_points(@@fmod_bgm[c], first, second, unit)
  1273.   end
  1274.   #--------------------------------------------------------------------------
  1275.   # * Play BGS
  1276.   #     name            : Name of the file
  1277.   #     volume          : Channel volume
  1278.   #     pitch           : Channel frequency
  1279.   #     position        : Starting position in milliseconds
  1280.   #     looping         : Does the BGS loop?
  1281.   #--------------------------------------------------------------------------
  1282.   def self.bgs_play(name, volume, pitch, c, position = 0, looping = true)
  1283.     al_pl = self.already_playing?(@@fmod_bgs[c], name, position)
  1284.     self.bgs_set_volume(volume, c) if al_pl
  1285.     return if al_pl
  1286.     # Now play the new BGS as a stream
  1287.     @@fmod_bgs[c] = self.play(name, volume, pitch, position, looping, true)
  1288.   end
  1289.   #--------------------------------------------------------------------------
  1290.   # * Stop and Dispose of BGS
  1291.   #--------------------------------------------------------------------------
  1292.   def self.bgs_stop(c)
  1293.     self.stop(@@fmod_bgs[c])
  1294.     @@fmod_bgs[c] = nil
  1295.   end
  1296.   #--------------------------------------------------------------------------
  1297.   # * Return BGS Length in Milliseconds
  1298.   #--------------------------------------------------------------------------
  1299.   def self.bgm_length(c)
  1300.     self.get_length(@@fmod_bgs[c])
  1301.   end
  1302.   #--------------------------------------------------------------------------
  1303.   # * Check if a BGS is Playing
  1304.   #--------------------------------------------------------------------------  
  1305.   def self.bgs_playing?(c)
  1306.     return self.playing?(@@fmod_bgs[c])
  1307.   end
  1308.   #--------------------------------------------------------------------------
  1309.   # * Get Current BGS Playing Position
  1310.   #--------------------------------------------------------------------------
  1311.   def self.bgs_position(c)
  1312.     return self.get_position(@@fmod_bgs[c])
  1313.   end
  1314.   #--------------------------------------------------------------------------
  1315.   # * Seek to New BGS Playing Position
  1316.   #--------------------------------------------------------------------------
  1317.   def self.bgs_set_position(new_pos, c)
  1318.     self.set_position(@@fmod_bgs[c], new_pos)
  1319.   end
  1320.   #--------------------------------------------------------------------------
  1321.   # * Get Current BGS Volume
  1322.   #--------------------------------------------------------------------------
  1323.   def self.bgs_volume(c)
  1324.     return self.get_volume(@@fmod_bgs[c])
  1325.   end
  1326.   #--------------------------------------------------------------------------
  1327.   # * Set BGS Volume
  1328.   #--------------------------------------------------------------------------
  1329.   def self.bgs_set_volume(volume, c)
  1330.     self.set_volume(@@fmod_bgs[c], volume)
  1331.   end
  1332.   #--------------------------------------------------------------------------
  1333.   # * Set BGS Mute
  1334.   #--------------------------------------------------------------------------
  1335.   def self.bgs_set_mute(c)
  1336.     self.set_mute(@@fmod_bgs[c])
  1337.   end
  1338.   #--------------------------------------------------------------------------
  1339.   # * Set BGS Unmute
  1340.   #--------------------------------------------------------------------------
  1341.   def self.bgs_set_unmute(c)
  1342.     self.set_unmute(@@fmod_bgs[c])
  1343.   end
  1344.   #--------------------------------------------------------------------------
  1345.   # * Get Current BGS Pan
  1346.   #--------------------------------------------------------------------------
  1347.   def self.bgs_pan(c)
  1348.     return self.get_pan(@@fmod_bgs[c])
  1349.   end
  1350.   #--------------------------------------------------------------------------
  1351.   # * Set BGS Pan
  1352.   #--------------------------------------------------------------------------
  1353.   def self.bgs_set_pan(pan, c)
  1354.     self.set_pan(@@fmod_bgs[c], pan)
  1355.   end
  1356.   #--------------------------------------------------------------------------
  1357.   # * Set Loop Points
  1358.   #     first           : Loop start point in milliseconds
  1359.   #     second          : Loop end point in milliseconds
  1360.   #     unit            : FMOD_TIMEUNIT for points
  1361.   #--------------------------------------------------------------------------
  1362.   def self.bgs_set_loop_points(first, second, c, unit = FModEx::FMOD_DEFAULT_UNIT)
  1363.     @@fmod_bgs[c] = self.set_loop_points(@@fmod_bgs[c], first, second, unit)
  1364.   end
  1365.   #--------------------------------------------------------------------------
  1366.   # * Play SE
  1367.   #     name            : Name of the file
  1368.   #     volume          : Channel volume
  1369.   #     pitch           : Channel frequency
  1370.   #--------------------------------------------------------------------------
  1371.   def self.se_play(name, volume, pitch, c)
  1372.     if (@@fmod_se[c] ||= []).size > @fmod.maxChannels
  1373.       se = @@fmod_se[c].shift
  1374.       self.stop(se)
  1375.     end
  1376.     # Load SE into memory and play it
  1377.     @@fmod_se[c] << self.play(name, volume, pitch, 0, false, false)
  1378.   end
  1379.   #--------------------------------------------------------------------------
  1380.   # * Stop and Dispose of all SEs
  1381.   #--------------------------------------------------------------------------
  1382.   def self.se_stop(c)
  1383.     for se in (@@fmod_se[c] ||= [])
  1384.       self.stop(se)
  1385.     end
  1386.     @@fmod_se[c].clear
  1387.   end
  1388.   #--------------------------------------------------------------------------
  1389.   # * Set SE Volume
  1390.   #--------------------------------------------------------------------------
  1391.   def self.se_set_volume(volume, c)
  1392.     for se in (@@fmod_se[c] ||= [])
  1393.       self.set_volume(se, volume)
  1394.     end
  1395.   end
  1396.   #--------------------------------------------------------------------------
  1397.   # * Set SE Mute
  1398.   #--------------------------------------------------------------------------
  1399.   def self.se_set_mute(c)
  1400.     for se in (@@fmod_se[c] ||= [])
  1401.       self.set_mute(se)
  1402.     end
  1403.   end
  1404.   #--------------------------------------------------------------------------
  1405.   # * Set SE Unmute
  1406.   #--------------------------------------------------------------------------
  1407.   def self.se_set_unmute(c)
  1408.     for se in (@@fmod_se[c] ||= [])
  1409.       self.set_unmute(se)
  1410.     end
  1411.   end
  1412.   #--------------------------------------------------------------------------
  1413.   # * Get Pan of one of the Current SEs
  1414.   #--------------------------------------------------------------------------
  1415.   def self.se_pan(c, n)
  1416.     return self.get_pan(@@fmod_se[c][n])
  1417.   end
  1418.   #--------------------------------------------------------------------------
  1419.   # * Set Pan for one of the Current SEs
  1420.   #--------------------------------------------------------------------------
  1421.   def self.se_set_pan(pan, c, n)
  1422.     self.set_pan(@@fmod_se[c][n], pan)
  1423.   end
  1424.   #--------------------------------------------------------------------------
  1425.   # * Get Rid of Non-Playing SEs
  1426.   #--------------------------------------------------------------------------  
  1427.   def self.se_clean(c)
  1428.     for se in (@@fmod_se[c] ||= [])
  1429.       unless self.playing?(se)
  1430.         self.stop(se)
  1431.         @@fmod_se[c].delete(se)
  1432.       end
  1433.     end
  1434.   end
  1435.   #--------------------------------------------------------------------------
  1436.   # * Check if There's Some SE in SE Array
  1437.   #--------------------------------------------------------------------------  
  1438.   def self.se_list_empty?(c)
  1439.     return (@@fmod_se[c] ||= []).empty?
  1440.   end
  1441.   #--------------------------------------------------------------------------
  1442.   # * Dispose of Everything
  1443.   #--------------------------------------------------------------------------  
  1444.   def self.dispose
  1445.     self.stop_all
  1446.     @fmod.dispose
  1447.   end
  1448.   #--------------------------------------------------------------------------
  1449.   # * Stop Everything
  1450.   #--------------------------------------------------------------------------  
  1451.   def self.stop_all
  1452.     for c in 1...APU::MAX_CHANNELS
  1453.       self.bgm_stop(c)
  1454.       self.bgs_stop(c)
  1455.       self.se_stop(c)
  1456.     end
  1457.   end
  1458.  
  1459.   #==============================================================================
  1460.   # ** Audio
  1461.   #------------------------------------------------------------------------------
  1462.   #  The module that carries out music and sound processing.
  1463.   #==============================================================================
  1464.  
  1465.   module Audio
  1466.     #--------------------------------------------------------------------------
  1467.     # * Constants
  1468.     #--------------------------------------------------------------------------
  1469.     BGM_FADE_IN_INCREMENT = 5     # BGM volume incremented 0.2 seconds
  1470.     #--------------------------------------------------------------------------
  1471.     # * Instance Variables
  1472.     #--------------------------------------------------------------------------
  1473.     @@bgm_fading_out = []#false       # BGM started fading out
  1474.     @@bgm_fade_decrement = []#0.0     # BGM volume decremented each update
  1475.     @@bgs_fading_out = []#false       # BGS started fading out
  1476.     @@bgs_fade_decrement = []#0.0     # BGS volume decremented each update
  1477.     @@me_fading_out = []#false        # ME started fading out
  1478.     @@me_fade_decrement = []#0.0      # ME volume decremented each update
  1479.     @@me_playing = []#false           # Is some ME playing?
  1480.     @@playing_bgm = []#nil            # BGM currently being played
  1481.     @@next_bgm = []#nil               # The BGM to be played after fading out
  1482.     @@next_bgm_position = []#0        # Starting position of next bgm
  1483.     @@next_bgs = []#nil               # The BGS to be played after fading out
  1484.     @@next_bgs_position = []#0        # Starting position of next bgm
  1485.     @@next_me = []#nil                # The ME to be played after fading
  1486.     @@memorized_bgm = []
  1487.     @@memorized_bgm_position = []
  1488.     @@bgm_fading_in = []
  1489.     #--------------------------------------------------------------------------
  1490.     # * Starts BGM Playback
  1491.     #     name            : Name of the file
  1492.     #     volume          : Channel volume
  1493.     #     pitch           : Channel frequency
  1494.     #     position        : Starting position in milliseconds
  1495.     #--------------------------------------------------------------------------
  1496.     def Audio.bgm_play(filename, volume, pitch, c, position = 0,
  1497.                         fade_in = false)
  1498.       if @@bgm_fading_out[c] and !fade_in
  1499.         @@next_bgm[c] = RPG::AudioFile.new(filename, volume, pitch)
  1500.         @@next_bgm_position[c] = position
  1501.         return
  1502.       end
  1503.       start_volume = volume
  1504.       if fade_in
  1505.         @@bgm_target_volume[c] = volume unless @@bgm_fading_in[c]
  1506.         @@bgm_fading_in[c] = true
  1507.         start_volume = 0
  1508.       end
  1509.       @@bgm_fading_out[c] = false
  1510.       # If a ME is playing we wait until it's over before playing BGM
  1511.       unless @@me_playing[c]
  1512.         FMod::bgm_play(filename, start_volume, pitch, c, position)
  1513.       end
  1514.       @@playing_bgm[c] = RPG::AudioFile.new(filename, volume, pitch)
  1515.       @@memorized_bgm[c] = @@playing_bgm[c]
  1516.       @@memorized_bgm_position[c] = position
  1517.     end
  1518.     #--------------------------------------------------------------------------
  1519.     # * Stops BGM Playback
  1520.     #--------------------------------------------------------------------------
  1521.     def Audio.bgm_stop(c)
  1522.       @@memorized_bgm[c] = nil
  1523.       @@playing_bgm[c] = nil
  1524.       @@bgm_fading_in[c] = false
  1525.       @@bgm_fading_out[c] = false
  1526.       # MEs are internally BGMs, but are stopped with me_stop instead
  1527.       if @@me_playing[c]
  1528.         return
  1529.       end
  1530.       FMod::bgm_stop(c)
  1531.     end
  1532.     #--------------------------------------------------------------------------
  1533.     # * Starts BGM fadeout.
  1534.     #     time            : Length of the fadeout in milliseconds.
  1535.     #--------------------------------------------------------------------------
  1536.     def Audio.bgm_fade(time, c)
  1537.       return if @@me_playing[c] or !FMod::bgm_playing?(c)
  1538.       @@bgm_fading_out[c] = true
  1539.       time = time / 1000
  1540.       @@bgm_fade_decrement[c] = FMod::bgm_volume(c) / (time * 10)
  1541.     end
  1542.     #--------------------------------------------------------------------------
  1543.     # * Get BGM Position
  1544.     #--------------------------------------------------------------------------
  1545.     def Audio.bgm_pos(c)
  1546.       FMod::bgm_position(c)
  1547.     end
  1548.     #--------------------------------------------------------------------------
  1549.     # * Starts BGS Playback
  1550.     #     name            : Name of the file
  1551.     #     volume          : Channel volume
  1552.     #     pitch           : Channel frequency
  1553.     #     position        : Starting position in milliseconds
  1554.     #--------------------------------------------------------------------------
  1555.     def Audio.bgs_play(filename, volume, pitch, c, position = 0)
  1556.       if @@bgs_fading_out[c]
  1557.         @@next_bgs[c] = RPG::AudioFile.new(filename, volume, pitch)
  1558.         @@next_bgs_position[c] = position
  1559.         return
  1560.       end
  1561.       FMod::bgs_play(filename, volume, pitch, c, position)
  1562.     end
  1563.     #--------------------------------------------------------------------------
  1564.     # * Stops BGS Playback
  1565.     #--------------------------------------------------------------------------
  1566.     def Audio.bgs_stop(c)
  1567.       FMod::bgs_stop(c)
  1568.       @@bgs_fading_out[c] = false
  1569.     end
  1570.     #--------------------------------------------------------------------------
  1571.     # * Starts BGS fadeout.
  1572.     #     time            : Length of the fadeout in milliseconds.
  1573.     #--------------------------------------------------------------------------
  1574.     def Audio.bgs_fade(time, c)
  1575.       return unless FMod::bgs_playing?(c)
  1576.       @@bgs_fading_out[c] = true
  1577.       time = time / 1000
  1578.       @@bgs_fade_decrement[c] = FMod::bgs_volume(c) / (time * 10)
  1579.     end
  1580.     #--------------------------------------------------------------------------
  1581.     # * Get BGS Position
  1582.     #--------------------------------------------------------------------------
  1583.     def Audio.bgs_pos(c)
  1584.       FMod::bgs_position(c)
  1585.     end
  1586.     #--------------------------------------------------------------------------
  1587.     # * Starts ME Playback
  1588.     #     name            : Name of the file
  1589.     #     volume          : Channel volume
  1590.     #     pitch           : Channel frequency
  1591.     #--------------------------------------------------------------------------
  1592.     def Audio.me_play(filename, volume, pitch, c)
  1593.       if @@me_fading_out[c]
  1594.         @@next_me[c] = RPG::AudioFile.new(filename, volume, pitch)
  1595.         return
  1596.       end
  1597.       if @@bgm_fading_out[c]
  1598.         self.bgm_stop(c)
  1599.       end
  1600.       # Memorize playing bgm
  1601.       if @@playing_bgm[c] and !@@me_playing[c]
  1602.         bgm = @@playing_bgm[c]
  1603.         @@playing_bgm[c] = RPG::AudioFile.new(bgm.name, FMod::bgm_volume(c), bgm.pitch)
  1604.         @@memorized_bgm[c] = @@playing_bgm[c]
  1605.         @@memorized_bgm_position[c] = FMod::bgm_position(c)
  1606.       end
  1607.       @@me_playing[c] = true
  1608.       FMod::bgm_play(filename, volume, pitch, c, 0, false)
  1609.     end
  1610.     #--------------------------------------------------------------------------
  1611.     # * Stops ME Playback
  1612.     #--------------------------------------------------------------------------
  1613.     def Audio.me_stop(c)
  1614.       return unless @@me_playing[c]
  1615.       @@me_playing[c] = false
  1616.       @@me_fading_out[c] = false
  1617.       # Play memorized bgm, fading in
  1618.       if @@memorized_bgm[c] and !@@bgm_fading_out[c]
  1619.         bgm = @@memorized_bgm[c]
  1620.         self.bgm_play(bgm.name, bgm.volume, bgm.pitch, @@memorized_bgm_position[c], true)
  1621.       else
  1622.         self.bgm_stop(c)
  1623.       end
  1624.     end
  1625.     #--------------------------------------------------------------------------
  1626.     # * Starts ME fadeout.
  1627.     #     time            : Length of the fadeout in milliseconds.
  1628.     #--------------------------------------------------------------------------
  1629.     def Audio.me_fade(time, c)
  1630.       return unless FMod::bgm_playing?(c)
  1631.       @@me_fading_out[c] = true
  1632.       time = time / 1000
  1633.       @@bgm_fade_decrement[c] = FMod::bgm_volume(c) / (time * 10)
  1634.     end
  1635.     #--------------------------------------------------------------------------
  1636.     # * Starts SE Playback
  1637.     #     name            : Name of the file
  1638.     #     volume          : Channel volume
  1639.     #     pitch           : Channel frequency
  1640.     #--------------------------------------------------------------------------
  1641.     def Audio.se_play(filename, volume, pitch, c)
  1642.       FMod::se_play(filename, volume, pitch, c)
  1643.     end
  1644.     #--------------------------------------------------------------------------
  1645.     # * Stops SE Playback
  1646.     #--------------------------------------------------------------------------
  1647.     def Audio.se_stop(c)
  1648.       FMod::se_stop(c)
  1649.     end
  1650.     #--------------------------------------------------------------------------
  1651.     # * Check whether in F1 settings music and/or sounds are disabled
  1652.     #--------------------------------------------------------------------------
  1653.     def Audio.check_sound
  1654.       open_key = Win32API.new('advapi32.dll', 'RegOpenKeyExA', 'LPLLP', 'L')
  1655.       query_value = Win32API.new('advapi32.dll', 'RegQueryValueExA', 'LPLPPP', 'L')
  1656.       close_key = Win32API.new('advapi32', 'RegCloseKey', 'L', 'L')
  1657.       key = 0.chr * 4
  1658.       # Open a HKEY_CURRENT_USER with KEY_READ attribute and save handle in key
  1659.       open_key.call(0x80000001, 'Software\Enterbrain\RGSS2', 0, 0x20019, key)
  1660.       key = key.unpack('l')[0]
  1661.       type = 0.chr * 4
  1662.       size = 0.chr * 4
  1663.       # Query to get string size
  1664.       query_value.call(key, "PlayMusic", 0, type, 0, size)
  1665.       data = ' ' * size.unpack('l')[0]
  1666.       # Query the string value itself using size
  1667.       query_value.call(key, "PlayMusic", 0, type, data, size)
  1668.       pm = data[0] != 0
  1669.       # Query to get string size
  1670.       query_value.call(key, "PlaySound", 0, type, 0, size)
  1671.       data = ' ' * size.unpack('l')[0]
  1672.       # Query the string value itself using size
  1673.       query_value.call(key, "PlaySound", 0, type, data, size)
  1674.       ps = data[0] != 0
  1675.       close_key.call(key)
  1676.       if (@@last_music_play_state ||= !pm) != pm
  1677.         for c in 1...APU::MAX_CHANNELS
  1678.           FMod::bgm_set_mute(c) if !pm
  1679.           FMod::me_set_mute(c) if !pm
  1680.           FMod::bgm_set_unmute(c) if pm
  1681.           FMod::me_set_unmute(c) if pm
  1682.         end
  1683.         @@last_music_play_state = pm
  1684.       end
  1685.       if (@@last_sound_play_state ||= !ps) != ps
  1686.         for c in 1...APU::MAX_CHANNELS
  1687.           FMod::bgs_set_mute(c) if !ps
  1688.           FMod::se_set_mute(c) if !ps
  1689.           FMod::bgs_set_unmute(c) if ps
  1690.           FMod::se_set_unmute(c) if ps
  1691.         end
  1692.         @@last_sound_play_state = ps
  1693.       end
  1694.     end
  1695.     #--------------------------------------------------------------------------
  1696.     # * Update ME Playback, SE Disposal and Fading, Called Each Frame
  1697.     #--------------------------------------------------------------------------
  1698.     def Audio.update
  1699.       self.check_sound if APU::READ_F1_SETTINGS
  1700.       for c in 1...APU::MAX_CHANNELS
  1701.         # Stop ME when it's over (and continue playing BGM)
  1702.         if @@me_playing[c]
  1703.           unless FMod::bgm_playing?(c)
  1704.             self.me_stop(c)
  1705.           end
  1706.         end
  1707.         # Remove any finished SEs
  1708.         unless FMod::se_list_empty?(c)
  1709.           FMod::se_clean(c)
  1710.         end
  1711.         if @@bgm_fading_in[c]
  1712.           # Stop fading when target is reached, otherwise increase volume
  1713.           if FMod::bgm_volume(c) >= @@bgm_target_volume[c]
  1714.             @@bgm_fading_in[c] = false
  1715.           else
  1716.             current_volume = FMod::bgm_volume(c) + BGM_FADE_IN_INCREMENT
  1717.             FMod::bgm_set_volume(current_volume, c)
  1718.           end
  1719.         end
  1720.         if FMod::bgm_playing?(c) and @@bgm_fading_out[c] and
  1721.             !@@me_playing[c]
  1722.           if FMod::bgm_volume(c) <= 0
  1723.             @@bgm_fading_out[c] = false
  1724.             self.bgm_stop(c)
  1725.             # If another BGM played while fading out, play it (most recent)
  1726.             if @@next_bgm[c]
  1727.               self.bgm_play(@@next_bgm[c].name, @@next_bgm[c].volume,
  1728.                             @@next_bgm[c].pitch, c, @@next_bgm_position[c])
  1729.               @@next_bgm[c] = nil
  1730.             end
  1731.           else
  1732.             current_volume = FMod::bgm_volume(c) - @@bgm_fade_decrement[c]
  1733.             FMod::bgm_set_volume(current_volume, c)
  1734.           end
  1735.         end
  1736.         if FMod::bgs_playing?(c) and @@bgs_fading_out[c]
  1737.           if FMod::bgs_volume(c) <= 0
  1738.             @@bgs_fading_out[c] = false
  1739.             self.bgs_stop(c)
  1740.             # If another BGS played while fading out, play it (most recent)
  1741.             if @@next_bgs[c]
  1742.               self.bgs_play(@@next_bgs[c].name, @@next_bgs[c].volume,
  1743.                             @@next_bgs[c].pitch, c, @@next_bgs_position[c])
  1744.               @@next_bgs[c] = nil
  1745.             end
  1746.           else
  1747.             current_volume = FMod::bgs_volume(c) - @@bgs_fade_decrement[c]
  1748.             FMod::bgs_set_volume(current_volume, c)
  1749.           end
  1750.         end
  1751.         if FMod::bgm_playing?(c) and @@me_fading_out[c]
  1752.           if FMod::bgm_volume(c) <= 0
  1753.             # If another ME played while fading out, play it (most recent)
  1754.             if @@next_me[c]
  1755.               self.me_play(@@next_me[c].name, @@next_me[c].volume, @@next_me[c].pitch, c)
  1756.               @@next_me[c] = nil
  1757.             else
  1758.               @@me_fading_out[c] = false
  1759.               self.me_stop(c)
  1760.             end
  1761.           else
  1762.             current_volume = FMod::bgm_volume(c) - @@bgm_fade_decrement[c]
  1763.             FMod::bgm_set_volume(current_volume, c)
  1764.           end
  1765.         end
  1766.       end
  1767.     end
  1768.   end
  1769. end
  1770.  
  1771. # Addition to let the DSP effects work.
  1772. module FMod
  1773.   def self.addDSP(sound_file, type)
  1774.     return -1 if type <= 0
  1775.     unless sound_file and sound_file.channel
  1776.       return -1
  1777.     end
  1778.     return sound_file.channel.addDSP(type)
  1779.   end
  1780.   def self.bgm_addDSP(c, type)
  1781.     return nil if c <= 0
  1782.     return nil unless @@fmod_bgm[c]
  1783.     return @@fmod_bgm[c].channel.DSP(self.addDSP(@@fmod_bgm[c], type))
  1784.   end
  1785.   def self.bgs_addDSP(c, type)
  1786.     return nil if c <= 0
  1787.     return nil unless @@fmod_bgs[c]
  1788.     return @@fmod_bgs[c].channel.DSP(self.addDSP(@@fmod_bgs[c], type))
  1789.   end
  1790.   def self.me_addDSP(c, type)
  1791.     return nil if c <= 0
  1792.     return nil unless @@fmod_me[c]
  1793.     return @@fmod_me[c].channel.DSP(self.addDSP(@@fmod_me[c], type))
  1794.   end
  1795.   def self.se_total(c)
  1796.     return 0 if c <= 0
  1797.     (@@fmod_se[c] ||= []).size
  1798.   end
  1799.   def self.se_addDSP(n, c, type)
  1800.     return nil if c <= 0
  1801.     return nil unless @@fmod_se[c][n]
  1802.     return @@fmod_se[c][n].channel.DSP(self.addDSP(@@fmod_se[c][n], type))
  1803.   end
  1804.   def self.removeDSP(sound_file, id)
  1805.     unless sound_file and sound_file.channel
  1806.       return
  1807.     end
  1808.     sound_file.channel.removeDSP(id)
  1809.   end
  1810.   def self.bgm_removeDSP(c, id)
  1811.     self.removeDSP(@@fmod_bgm[c], id)
  1812.   end
  1813.   def self.bgs_removeDSP(c, id)
  1814.     self.removeDSP(@@fmod_bgs[c], id)
  1815.   end
  1816.   def self.me_removeDSP(c, id)
  1817.     self.removeDSP(@@fmod_me[c], id)
  1818.   end
  1819.   def self.se_removeDSP(c, id)
  1820.     for se in (@@fmod_se[c] ||= [])
  1821.       self.removeDSP(se, id)
  1822.     end
  1823.   end
  1824.   def self.bgm_DSP(c)
  1825.     return 0 if c <= 0
  1826.     return 0 unless @@fmod_bgm[c]
  1827.     return @@fmod_bgm[c].channel.dsps.size
  1828.   end
  1829.   def self.bgs_DSP(c)
  1830.     return 0 if c <= 0
  1831.     return 0 unless @@fmod_bgs[c]
  1832.     return @@fmod_bgs[c].channel.dsps.size
  1833.   end
  1834.   def self.me_DSP(c)
  1835.     return 0 if c <= 0
  1836.     return 0 unless @@fmod_me[c]
  1837.     return @@fmod_me[c].channel.dsps.size
  1838.   end
  1839.   def self.se_DSP(c)
  1840.     return 0 if c <= 0
  1841.     return 0 unless @@fmod_se[c]
  1842.     return 0 unless @@fmod_se[c][0]
  1843.     return @@fmod_se[c][0].channel.dsps.size
  1844.   end
  1845.   def self.setParameter(sound_file, dsp_id, param_id, value)
  1846.     unless sound_file and sound_file.channel
  1847.       return
  1848.     end
  1849.     dsp = sound_file.channel.DSP(dsp_id)
  1850.     return if dsp.nil?
  1851.     dsp.setParameter(param_id, value)
  1852.   end
  1853.   def self.bgm_setParameter(c, dsp_id, param_id, value)
  1854.     self.setParameter(@@fmod_bgm[c], dsp_id, param_id, value)
  1855.   end
  1856.   def self.bgs_setParameter(c, dsp_id, param_id, value)
  1857.     self.setParameter(@@fmod_bgs[c], dsp_id, param_id, value)
  1858.   end
  1859.   def self.me_setParameter(c, dsp_id, param_id, value)
  1860.     self.setParameter(@@fmod_me[c], dsp_id, param_id, value)
  1861.   end
  1862.   def self.se_setParameter(c, dsp_id, param_id, value)
  1863.     for se in (@@fmod_se[c] ||= [])
  1864.       self.setParameter(se, dsp_id, param_id, value)
  1865.     end
  1866.   end
  1867.   def self.getParameter(sound_file, dsp_id, param_id = 0)
  1868.     unless sound_file and sound_file.channel
  1869.       return - 1
  1870.     end
  1871.     dsp = sound_file.channel.DSP(dsp_id)
  1872.     return -1 if dsp.nil?
  1873.     dsp.getParameter(param_id)
  1874.   end
  1875.   def self.bgm_getParameter(c, dsp_id, param_id = 0)
  1876.     self.getParameter(@@fmod_bgm[c], dsp_id, param_id)
  1877.   end
  1878.   def self.bgs_getParameter(c, dsp_id, param_id = 0)
  1879.     self.getParameter(@@fmod_bgs[c], dsp_id, param_id)
  1880.   end
  1881.   def self.me_getParameter(c, dsp_id, param_id = 0)
  1882.     self.getParameter(@@fmod_me[c], dsp_id, param_id)
  1883.   end
  1884.   def self.se_getParameter(c, dsp_id, param_id = 0)
  1885.     self.getParameter(@@fmod_se[c][0], dsp_id, param_id)
  1886.   end
  1887.   def self.getNumParameters(sound_file, dsp_id)
  1888.     unless sound_file and sound_file.channel
  1889.       return 0
  1890.     end
  1891.     dsp = sound_file.channel.DSP(dsp_id)
  1892.     return 0 if dsp.nil?
  1893.     dsp.getNumParameters
  1894.   end
  1895.   def self.bgm_getNumParameters(c, dsp_id)
  1896.     self.getNumParameters(@@fmod_bgm[c], dsp_id)
  1897.   end
  1898.   def self.bgs_getNumParameters(c, dsp_id)
  1899.     self.getNumParameters(@@fmod_bgs[c], dsp_id)
  1900.   end
  1901.   def self.me_getNumParameters(c, dsp_id)
  1902.     self.getNumParameters(@@fmod_me[c], dsp_id)
  1903.   end
  1904.   def self.se_getNumParameters(c, dsp_id)
  1905.     self.getNumParameters(@@fmod_se[c][0], dsp_id)
  1906.   end
  1907.   def self.getType(sound_file, dsp_id)
  1908.     unless sound_file and sound_file.channel
  1909.       return 0
  1910.     end
  1911.     dsp = sound_file.channel.DSP(dsp_id)
  1912.     return 0 if dsp.nil?
  1913.     dsp.getType
  1914.   end
  1915.   def self.bgm_getType(c, dsp_id)
  1916.     self.getType(@@fmod_bgm[c], dsp_id)
  1917.   end
  1918.   def self.bgs_getType(c, dsp_id)
  1919.     self.getType(@@fmod_bgs[c], dsp_id)
  1920.   end
  1921.   def self.me_getType(c, dsp_id)
  1922.     self.getType(@@fmod_me[c], dsp_id)
  1923.   end
  1924.   def self.se_getType(c, dsp_id)
  1925.     self.getType(@@fmod_se[c][0], dsp_id)
  1926.   end
  1927.   def self.getDSPTypeByName(effect)
  1928.     return FModEx::FMOD_DSP_TYPES.index(effect.upcase)
  1929.   end
  1930.   def self.addEffect(dsp, type, *params)
  1931.     return if dsp.nil? || type.nil? || type == 0
  1932.     max_p = dsp.getNumParameters
  1933.     if params.size > max_p
  1934.       p "This effect requires max #{max_p.to_s} arguments, you passed #{params.size.to_s}."
  1935.       p "Default will be used."
  1936.       return
  1937.     end
  1938.     id = 0
  1939.     for par in params
  1940.       dsp.setParameter(id, par) if par != nil
  1941.       id += 1
  1942.     end
  1943.   end
  1944.   module Audio
  1945.     def self.bgm_add_effect(effect, c, *params)
  1946.       t = FMod::getDSPTypeByName(effect)
  1947.       FMod::addEffect(dsp = FMod::bgm_addDSP(c, t), t, *params)
  1948.       return dsp
  1949.     end
  1950.     def self.bgs_add_effect(effect, c, *params)
  1951.       t = FMod::getDSPTypeByName(effect)
  1952.       FMod::addEffect(dsp = FMod::bgs_addDSP(c, t), t, *params)
  1953.       return dsp
  1954.     end
  1955.     def self.me_add_effect(effect, c, *params)
  1956.       t = FMod::getDSPTypeByName(effect)
  1957.       FMod::addEffect(dsp = FMod::me_addDSP(c, t), t, *params)
  1958.       return dsp
  1959.     end
  1960.     def self.se_add_effect(effect, c, *params)
  1961.       t = FMod::getDSPTypeByName(effect)
  1962.       for n in 0...FMod::se_total(c)
  1963.         FMod::addEffect(FMod::se_addDSP(n, c, t), t, *params)
  1964.       end
  1965.     end
  1966.   end
  1967. end
  1968.  
  1969. # Methods: (following comments are for searching purposes)
  1970. class << Audio
  1971. #module Audio#def self.bgm_play() <- aliased
  1972. #module Audio#def self.bgm_stop() <- aliased
  1973. #module Audio#def self.bgm_fade() <- aliased
  1974. #module Audio#def self.bgs_play() <- aliased
  1975. #module Audio#def self.bgs_stop() <- aliased
  1976. #module Audio#def self.bgs_fade() <- aliased
  1977. #module Audio#def self.me_play() <- aliased
  1978. #module Audio#def self.me_stop() <- aliased
  1979. #module Audio#def self.me_fade() <- aliased
  1980. #module Audio#def self.se_play() <- aliased
  1981. #module Audio#def self.se_stop() <- aliased
  1982.   ["bgm_play","bgm_stop","bgm_fade",#"bgm_pos",
  1983.    "bgs_play","bgs_stop","bgs_fade",#"bgs_pos",
  1984.    "me_play","me_stop","me_fade",
  1985.    "se_play","se_stop"].each { |m|
  1986.     new = (m+"_b4_apu").to_sym
  1987.     old = m.to_sym
  1988.     alias_method(new, old) unless method_defined?(new)
  1989.     define_method(old){ |*args|
  1990.       if $game_variables[APU::CURRENT_CHANNEL_VARIABLE] == 0 || APU::CURRENT_CHANNEL_VARIABLE > APU::MAX_CHANNELS
  1991.         self.method(new).call(*args)
  1992.       else
  1993.         args << $game_variables[APU::CURRENT_CHANNEL_VARIABLE]
  1994.         FMod::Audio.method(old).call(*args)
  1995.       end
  1996.       }
  1997.     }
  1998. end
  1999.  
  2000. # Let's update in Scene_Base!
  2001. class Scene_Base
  2002.   alias_method(:update_b4_apu, :update) unless method_defined?(:update_b4_apu)
  2003. #class Scene_Base#def update() <- aliased
  2004.   def update
  2005.     if !@audio_sleep || @audio_sleep == 4
  2006.       FMod::Audio.update
  2007.       @audio_sleep = 0
  2008.     else
  2009.       @audio_sleep += 1
  2010.     end
  2011.     update_b4_apu
  2012.   end
  2013. end
  2014.  
  2015. FMod::stop_all # Stop all the playing audio at start (think about F12)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement