Advertisement
mikb89

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

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