Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
- # MCI Audio Player
- # Author: ForeverZer0
- # Version: 1.3
- # Date: 7.5.2014
- #=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
- # VERSION HISTORY
- #=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
- # v.1.0 4.29.2012
- # - Original Release
- #
- # v.1.1 4.29.2012
- # - Fixed a typo
- # - Added instructions for ensuring all channels stop when player closes game
- # - Added RPG Maker VX compatibility
- # - Added RPG Maker VX Ace compatibility
- # - Fixed duration to ensure a minimum of 1 frame, else nothing happens
- # - Added "start position" argument to "play" call. This ensures VXA
- # functionality, as well as a minor improvement to the system
- #
- # v.1.2 7.8.2012
- # - Fixed incorrect filepaths that mixed forward and backward slashes
- # - Fixed a bug that would cause full paths to files not to play sometimes
- # - Added an enumerator for iterating Audio mixers ("Audio.each_mixer")
- # - Added methods to Game_System for memorizing/restoring audio
- #
- # v.1.3 7.5.2014
- # - Fixed bug that would cause BGM/BGS to restart from beginning on transfer
- # to new map with same BGM or BGS
- #=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
- #
- # Introduction:
- #
- # This script acts a either a drop-in replacement or enhancement to the
- # built-in Audio module. It contains a plethora of functions that the default
- # player lacks, such as seek, pause, record, fade in/out, and many more. It
- # works by totally bypassing the built-in audio library, and directly
- # accessing Window's "winmm" library using Ruby's Win32API. This library is
- # home to the Media Control Interface, or MCI, which provides functions for
- # manipulating audio and video. This allows for much more control over sound,
- # and the ability for RPG Maker to play additional audio formats, since all
- # that is needed is to distribute the appropriate codecs along with your game
- # in order to use them.
- #
- # Features:
- #
- # - Full seek functions, accurate to the millisecond
- # - Ability to read file lengths
- # - Ability to pause and resume a playing file
- # - Functions for transitioning from one volume to another over a given number
- # frames, to both lower and higher volumes
- # - Can create as many mixers as needed, which allows to play multiple sounds
- # simultaneously, which gives the ability to play more than one BGM or BGS
- # at a time
- # - Record function to capture input from the user (.wav format only)
- # - Ability to set volume to left and right speaker independently
- # - Mute functions
- # - Can set the speed of playback
- # - Ability to set treble and bass (Not all devices support this)
- # - Searches RTP files and uses them automatically if file is not local
- # - Easy access for additional calls to the Media Control Interface
- # - No porting external libraries with your game, all functioning is done
- # within the script and the operating system
- # - Can use any audio format you wish, so long as the appropriate codec is
- # installed on the host computer
- # - Full compatibility with RMXP, RMVX, and RMVX Ace
- # - Memorize and restore all/some channels
- #
- # Instructions:
- #
- # - Scroll below to make the setting for what RPG Maker version you are using
- # - Place script anywhere above Main
- # - Only one setting (below) to set the MCI player as default audio player,
- # which is by default "true". If false, the MCI player will only be used for
- # special circumstances via script calls
- # - There are a whole bunch of new script calls available for the Audio module
- # There is full documentation if you look below, which I reccomend that you
- # look at if you choose to use this script, but most are self-explanatory.
- # Here is a partial list, words in caps are arguments that need replaced.
- #
- # - play(FILENAME, VOLUME, SPEED, REPEAT)
- # - pause
- # - restart
- # - pause
- # - resume
- # - stop
- # - close
- # - volume/volume=
- # - set_volume_left(VOLUME)
- # - set_volume_right(VOLUME)
- # - mute
- # - fade(DURATION, FRAMES)
- # - speed/speed=
- # - duration
- # - position/position=
- # - seek(MILLISECOND)
- # - playing?
- # - paused?
- # - recording?
- # - treble/treble=
- # - bass/bass=
- # - status
- # - record(BITS_PER_SAMPLE, SAMPLERATE, CHANNELS)
- # - save(FILENAME)
- #
- # To play a sound on a mixer is pretty straightforward, you don't even need
- # to create a mixer first, that is done automatically. All you need to do
- # is use a unique name for each mixer you want to control, and use the
- # above methods on it. For example, to play a file:
- #
- # Audio['myName'].play('myFile.mp3')
- #
- # From here on, you can access the same mixer using 'myName' as the key to
- # perform further actions. Such as...
- #
- # Audio['myName'].pause - Pauses playback
- # Audio['myName'].resume - Resumes playback
- # Audio['myName'].volume = 50 - Sets volume to fifty
- # Audio['myName'].fade(80, 0) - Fades volume to 0 in 80 frames
- # Audio['myName'].fade(240, 100) - Transitions volume to 100 in 6 seconds
- # Audio['myName'].record - Begins recording
- # Audio['myName'].save('file.wav') - Saves recorded file
- # Audio['myName'].seek(4500) - Sets playback position to 4.5 seconds
- #
- # As you can see, all mixers are accessed in a hash-like way via the Audio
- # module. By default, 'BGM', 'BGS', 'ME', and 'SE' are mixers used as
- # replacements for the built-in audio library, so use names other them if
- # creating additional mixers.
- #
- # There is also a script call to make a call using mciSendString if you are
- # familiar with the library at all. I simplified it down a bit, but it can
- # be used with the following snippet:
- #
- # Audio.mci_eval(MCI_COMMAND)
- #
- # For more information about using MCI commands, please see the full
- # documentation at MSDN.
- #
- # http://msdn.microsoft.com/en-us/library/windows/desktop/dd743572(v=vs.85).aspx
- #
- # There are also a few methods available via the Game_System class for
- # memorizing/restoring audio at its current position.
- #
- # ex.
- #
- # $game_system.memorize_audio - Memorize all channels
- # $game_system.memorize_audio('BGM') - Memorize BGM channel
- # $game_system.memorize_audio('BGM', 'BGS') - Memorize BGM and BGS channels
- # $game_system.restore_audio - Restore all channels
- # $game_system.restore_audio('BGM') - Restore BGM channel
- # $game_system.restore_audio('BGM', 'BGS') - Restore BGM and BGS channels
- #
- # You can use as many arguments for each method as you wish. Omitting
- # arguments will simply have it memorize/restore all channels.
- #
- # Compatibility:
- #
- # - Not tested on Linux running under Wine. If anyone tests this, let me know.
- # - Not compatible with DREAM.
- #
- # Credits/Thanks:
- #
- # - ForeverZer0, for the script
- #
- # Authors Notes:
- #
- # - Changing pitch will be different. MCI does not have a function for this,
- # so I am changing the speed as a generic substitute. Changing the speed
- # does change the pitch, but it true sound pitch alters the sampling rate
- # as well, which this player does not. You have a couple alternatives if
- # you absolutely need the pitch change:
- #
- # 1. Edit the files using an external editor and import it
- # 2. Use the default system to play such sounds using the alias names.
- #
- #=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
- #===============================================================================
- # ** Audio
- #===============================================================================
- module Audio
- # Set true/false if the MCI Player should be the default audio controller. It
- # will maintain all the same functionality as the standard control, and all
- # normal calls to the Audio module will work as normal, but it will also
- # extend the its functionality. If false, RMXP's standard library will be used
- # and this player will only be used for custom mixers.
- MCI_DEFAULT = true
- # Due to vast differences in how the format is handled, some of this player's
- # features do not work with MIDI, most notably volume control, which also
- # effects fading. I have created alternate controls to control MIDI volume,
- # but they can only work with the sacrifice of many other functions, and the
- # volume applies to ALL playing MIDIs, not just the one the volume is applied
- # to. I decided this was not worth it, so I omitted volume control. If you
- # are willing to make them sacrifices, you can enable this mode by setting
- # MIDI_MODE to true. If you your game relies heavily on MIDI, but you still
- # would like to use this script, there are conversion programs available,
- # which I can assist you with if need be.
- MIDI_MODE = false
- # Enter the code for the version of RPG Maker you are using this script for.
- # RMXP = 0
- # RMVX = 1
- # RMVXA = 2
- RPG_VERSION = 0
- #===============================================================================
- # ** Mixer
- #-------------------------------------------------------------------------------
- # This class acts a wrapper for Window's Media Control Interface (MCI).
- #===============================================================================
- MCISendString = Win32API.new('winmm', 'mciSendString', 'PPLL', 'L')
- MIDIOutSetVolume = Win32API.new('winmm', 'midiOutSetVolume', 'LL', 'L')
- class Mixer
- attr_reader :name # The arbitrary name of the mixer
- attr_reader :filepath # The path of the currently loaded file
- attr_reader :repeat # Flag if playback is set to repeat
- attr_reader :filename # Name of the file being played
- #---------------------------------------------------------------------------
- # * Object Initialization
- # name : The unique name of the mixer
- #---------------------------------------------------------------------------
- def initialize(name)
- @name = name
- @fade_duration = 0
- @fade_volume = 0
- @opened = false
- @repeat = false
- @midi = false
- end
- #---------------------------------------------------------------------------
- # * Load a file and prepare for playback
- # filename : The path to the file.
- #---------------------------------------------------------------------------
- def open(filename)
- if File.exists?(filename)
- path = filename
- else
- path = Dir::glob(filename + '.*')[0]
- if path == nil
- directory = File.dirname(filename)
- if RTP.subfolder?(directory)
- file = File.basename(filename)
- path = RTP[directory].find {|f| File.basename(f, '.*') == file }
- end
- end
- end
- @filepath = path.gsub(/\\/, '/')
- if MIDI_MODE && ['.mid', '.midi'].include?(File.extname(path))
- @midi = true
- cmd = "open \"#{path}\" type sequencer alias #{@name}"
- else
- cmd = "open \"#{path}\" type mpegvideo alias #{@name}"
- end
- MCISendString.call(cmd, nil, 0, 0)
- MCISendString.call("set #{@name} time format milliseconds", nil, 0, 0)
- MCISendString.call("set #{@name} seek exactly on", nil, 0, 0)
- @opened = true
- end
- #---------------------------------------------------------------------------
- # * Began playback on the mixer
- # filename : The name of the file to play
- # volume : The volume to play the file at
- # speed : The speed to play the the fule at
- # repeat : Flag if playback should loop after done playing
- # start : The position, in milliseconds, to begin playback at
- #---------------------------------------------------------------------------
- def play(filename, volume = 100, speed = 100, repeat = false, start = 0)
- @filename = filename
- self.close
- self.open(filename)
- self.speed = speed
- if MIDI_MODE && @midi
- @midi_master = @midi_right = @midi_left = volume * 10
- self.set_volume(volume)
- MCISendString.call("play #{@name} from 0", nil, 0, 0)
- return
- end
- self.set_volume(volume)
- @repeat = repeat
- if start != 0
- MCISendString.call("seek #{@name} to #{start}", nil, 0, 0)
- else
- MCISendString.call("seek #{@name} to start", nil, 0, 0)
- end
- MCISendString.call("play #{@name}#{repeat ? ' repeat' : ''}", nil, 0, 0)
- end
- #---------------------------------------------------------------------------
- # * Restarts playback of the currently loaded file from the beginning
- #---------------------------------------------------------------------------
- def restart
- MCISendString.call("seek #{@name} to start", nil, 0, 0)
- MCISendString.call("play #{@name}#{repeat ? ' repeat' : ''}", nil, 0, 0)
- end
- #---------------------------------------------------------------------------
- # * Pause playback and maintain current position
- #---------------------------------------------------------------------------
- def pause
- MCISendString.call("pause #{@name}", nil, 0, 0)
- end
- #---------------------------------------------------------------------------
- # * Resume playback on a paused mixer from previous position
- #---------------------------------------------------------------------------
- def resume
- MCISendString.call("resume #{@name}", nil, 0, 0)
- end
- #---------------------------------------------------------------------------
- # * Stops playback, setting position back to the start
- #---------------------------------------------------------------------------
- def stop
- MCISendString.call("seek #{@name} to start", nil, 0, 0)
- MCISendString.call("stop #{@name}", nil, 0, 0)
- end
- #---------------------------------------------------------------------------
- # * Closes the opened file and frees it from memory
- #---------------------------------------------------------------------------
- def close
- MCISendString.call("close #{@name}", nil, 0, 0)
- @filepath = nil
- @opened = false
- end
- #---------------------------------------------------------------------------
- # * Gets the actual volume of the mixer, an integer from 0 to 1000
- #---------------------------------------------------------------------------
- def real_volume
- if MIDI_MODE && @midi
- return @midi_master
- else
- data = "\0" * 128
- MCISendString.call("status #{@name} volume", data, 128, 0)
- return data.delete("\0").to_i
- end
- end
- #---------------------------------------------------------------------------
- # * Sets the actual volume of the mixer
- # value : The volume level, integer between 0 and 1000
- #---------------------------------------------------------------------------
- def real_volume=(value)
- self.set_real_volume(value)
- end
- #---------------------------------------------------------------------------
- # * Sets the actual volume of the mixer
- # value : The volume level, integer between 0 and 1000
- #---------------------------------------------------------------------------
- def set_real_volume(value)
- vol = [0, [value, 1000].min].max
- if MIDI_MODE && @midi
- @midi_master = value
- self.set_midi_volume(value, value)
- else
- MCISendString.call("setaudio #{@name} volume to #{vol}", nil, 0, 0)
- end
- end
- #---------------------------------------------------------------------------
- # * Returns the volume of the mixer
- #---------------------------------------------------------------------------
- def volume
- return self.real_volume / 10
- end
- #---------------------------------------------------------------------------
- # * Sets the volume of the mixer
- # value : The volume level, integer between 0 and 100
- #---------------------------------------------------------------------------
- def volume=(value)
- value = [0, [value, 100].min].max
- self.set_real_volume(value * 10)
- end
- #---------------------------------------------------------------------------
- # * Sets the volume of the mixer
- # value : The volume level, integer between 0 and 100
- #---------------------------------------------------------------------------
- def set_volume(value)
- value = [0, [value, 100].min].max
- self.set_real_volume(value * 10)
- end
- #---------------------------------------------------------------------------
- # * Set the volume of the mixer in the left speaker only
- # value : Volume level, integer between 0 and 100
- #---------------------------------------------------------------------------
- def set_volume_left(value)
- vol = [0, [value * 10, 1000].min].max
- if MIDI_MODE && @midi
- self.set_midi_volume(value, @midi_right)
- else
- MCISendString.call("setaudio #{@name} left volume to #{vol}", nil, 0, 0)
- end
- end
- #---------------------------------------------------------------------------
- # * Set the volume of the mixer in the right speaker only
- # value : Volume level, integer between 0 and 100
- #---------------------------------------------------------------------------
- def set_volume_right(value)
- vol = [0, [value * 10, 1000].min].max
- if MIDI_MODE && @midi
- self.set_midi_volume(@midi_left, value)
- else
- MCISendString.call("setaudio #{@name} right volume to #{vol}", nil, 0, 0)
- end
- end
- #---------------------------------------------------------------------------
- # * Special handling for adjusting MIDI volume. MCI cannot handle the volume
- # for this format directly, so we need to precalculate the channels and
- # make the call to the MIDI synthesizer ourselves.
- # left : The volume of the left channel
- # right : The volume of the right channel
- #
- # NOTE:
- # It is recommended that you do not call this method directly.
- #---------------------------------------------------------------------------
- def set_midi_volume(left, right)
- @midi_left, @midi_right = left, right
- left = (0xFFFF * (left / 1000.0)).round
- right = (0xFFFF * (right / 1000.0)).round
- vol = right.to_s(16) + left.to_s(16)
- MIDIOutSetVolume.call(0, vol.to_i(16))
- end
- #---------------------------------------------------------------------------
- # * Mutes sound from the mixer
- # bool : True/false flag to mute/unmute sound
- #---------------------------------------------------------------------------
- def mute(bool)
- MCISendString.call("setaudio #{@name} #{bool ? 'on' : 'off'}", nil, 0, 0)
- end
- #---------------------------------------------------------------------------
- # * Transition volume to another volume
- # duration : The number of frames the transition will take
- # target : The target volume to transition to
- #---------------------------------------------------------------------------
- def fade(duration, target = 0)
- @fade_volume = target * 10
- @fade_duration = [duration, 1].max
- end
- #---------------------------------------------------------------------------
- # * Returns the current speed of playback (100 = normal)
- #---------------------------------------------------------------------------
- def speed
- data = "\0" * 256
- MCISendString.call("status #{@name} speed", data, 256, 0)
- data.delete!("\0")
- return data.to_i / 10
- end
- #---------------------------------------------------------------------------
- # * Set the current speed of playback
- # value : The rate of playback to set
- #---------------------------------------------------------------------------
- def speed=(value)
- set_speed(value)
- end
- #---------------------------------------------------------------------------
- # * Set the current speed of playback
- # value : The rate of playback to set
- #---------------------------------------------------------------------------
- def set_speed(value)
- value = [0, [2000, value * 10].min].max
- MCISendString.call("set #{@name} speed #{value}", nil, 0, 0)
- end
- #---------------------------------------------------------------------------
- # * Gets the length of the loaded file in milliseconds
- #---------------------------------------------------------------------------
- def duration
- if self.playing?
- length = "\0" * 256
- MCISendString.call("status #{@name} length", length, 256, 0)
- length.delete!("\0")
- return length.to_i
- end
- return 0
- end
- #---------------------------------------------------------------------------
- # * Returns duration as a string in normal MM:SS format
- #---------------------------------------------------------------------------
- def duration_string
- seconds = self.duration / 1000
- return sprintf('%2d:%02d', seconds / 60, seconds % 60)
- end
- #---------------------------------------------------------------------------
- # * Returns the current position of playback, in milliseconds
- #---------------------------------------------------------------------------
- def position
- pos = "\0" * 256
- MCISendString.call("status #{@name} position", pos, 256, 0)
- pos.delete!("\0")
- return pos.to_i
- end
- #---------------------------------------------------------------------------
- # * Returns current position as a string in normal MM:SS format
- #---------------------------------------------------------------------------
- def position_string
- seconds = self.position / 1000
- return sprintf('%2d:%02d', seconds / 60, seconds % 60)
- end
- #---------------------------------------------------------------------------
- # * Sets the current playback position
- # value : The time in milliseconds to set current playback
- #---------------------------------------------------------------------------
- def position=(value)
- self.seek(value)
- end
- #---------------------------------------------------------------------------
- # * Sets the current playback position
- # value : The time in milliseconds to set current playback
- #---------------------------------------------------------------------------
- def seek(value)
- cmd = "#{self.playing? ? 'play' : 'seek'} #{@name} from #{value}"
- MCISendString.call(cmd, nil, 0, 0)
- end
- #---------------------------------------------------------------------------
- # * Returns tha "playing" status of the mixer
- #---------------------------------------------------------------------------
- def playing?
- return self.status == 'playing'
- end
- #---------------------------------------------------------------------------
- # * Returns tha "paused" status of the mixer
- #---------------------------------------------------------------------------
- def paused?
- return self.status == 'paused'
- end
- #---------------------------------------------------------------------------
- # * Returns true/false if file is currently loaded for playback
- #---------------------------------------------------------------------------
- def opened?
- return @opened
- end
- #---------------------------------------------------------------------------
- # * Returns true/false if mixer is currently recording
- #---------------------------------------------------------------------------
- def recording?
- return self.status == 'recording'
- end
- #---------------------------------------------------------------------------
- # * Returns the mixer's bass value
- #---------------------------------------------------------------------------
- def treble
- data = "\0" * 128
- MCISendString.call("status #{@name} treble", data, 128, 0)
- data.delete!("\0")
- return data.to_i
- end
- #---------------------------------------------------------------------------
- # * Set mixer treble
- # value : Treble value, integer between 0 and 1000
- #---------------------------------------------------------------------------
- def treble=(value)
- set_treble(value)
- end
- #---------------------------------------------------------------------------
- # * Set mixer treble
- # value : Treble value, integer between 0 and 1000
- #---------------------------------------------------------------------------
- def set_treble(value)
- value = [0, [value, 1000].min].max
- MCISendString.call("setaudio #{@name} treble to #{value}", nil, 0, 0)
- end
- #---------------------------------------------------------------------------
- # * Returns the mixer's bass value
- #---------------------------------------------------------------------------
- def bass
- data = "\0" * 128
- MCISendString.call("status #{@name} bass", data, 128, 0)
- data.delete!("\0")
- return data.to_i
- end
- #---------------------------------------------------------------------------
- # * Set mixer bass
- # value : Bass value, integer between 0 and 1000
- #---------------------------------------------------------------------------
- def bass=(value)
- set_bass(value)
- end
- #---------------------------------------------------------------------------
- # * Set mixer bass
- # value : Bass value, integer between 0 and 1000
- #---------------------------------------------------------------------------
- def set_bass(value)
- value = [0, [value, 1000].min].max
- MCISendString.call("setaudio #{@name} bass to #{value}", nil, 0, 0)
- end
- #---------------------------------------------------------------------------
- # * Gets the current status
- #---------------------------------------------------------------------------
- def status
- data = "\0" * 256
- MCISendString.call("status #{@name} mode", data, 256, 0)
- return data.delete("\0")
- end
- #---------------------------------------------------------------------------
- # * Begins recording from the input, typically the PC's microphone
- # bits_ps : Bits per sample the file will be recorded at
- # sample_rate : Sample rate the the file will be recorded at
- # channels : Number of channels that will be opened for recording
- #
- # * WARNING *
- # Make sure that "stop", "close" or "save" is performed on this mixer
- # within a reasonable of amount of time. While the mixer is recording,
- # the file is held in RAM, which will become very large if left without
- # closing it, and eventually slow down the PC and/or crash the game.
- # Basically I'm just saying "don't forget you are recording"
- #---------------------------------------------------------------------------
- def record(bits_ps = 16, sample_rate = 44100, channels = 2)
- self.close
- MCISendString.call("open new type waveaudio alias #{@name}", nil, 0, 0)
- MCISendString.call("set #{@name} bitspersample #{bits_ps}", nil, 0, 0)
- MCISendString.call("set #{@name} samplespersec #{sample_rate}", nil, 0, 0)
- MCISendString.call("set #{@name} channels #{channels}", nil, 0, 0)
- MCISendString.call("record #{@name}", nil, 0, 0)
- end
- #---------------------------------------------------------------------------
- # * Saves a recording into WAV format
- # filename : The path of the file t save, must have '.wav' extension
- #---------------------------------------------------------------------------
- def save(filename)
- if self.recording?
- MCISendString.call("stop #{@name}", nil, 0, 0)
- end
- if File.extname(filename) != '.wav'
- filename += '.wav'
- end
- MCISendString.call("save #{@name} #{filename}", nil, 0, 0)
- MCISendString.call("close #{@name}", nil, 0, 0)
- end
- #---------------------------------------------------------------------------
- # * Frame update
- #---------------------------------------------------------------------------
- def update
- if @fade_duration >= 1
- d = @fade_duration
- self.set_real_volume((self.real_volume * (d - 1) + @fade_volume) / d)
- @fade_duration -= 1
- end
- end
- end
- #===============================================================================
- # ** Audio
- #-------------------------------------------------------------------------------
- # The metaclass of the Audio module. This class is a wrapper between the default
- # audio controls and the MCI Player controls.
- #===============================================================================
- class << self
- #---------------------------------------------------------------------------
- # * Use MCI Player play function
- #---------------------------------------------------------------------------
- alias mci_bgm_play bgm_play
- def bgm_play(filename, volume = 100, pitch = 100, start = 0)
- if MCI_DEFAULT
- mixer_play('BGM', filename, volume, pitch, true, start)
- elsif RPG_VERSION == 2
- mci_bgm_play(filename, volume, pitch, start)
- else
- mci_bgm_play(filename, volume, pitch)
- end
- end
- #---------------------------------------------------------------------------
- # * Use MCI Player play function
- #---------------------------------------------------------------------------
- alias mci_bgs_play bgs_play
- def bgs_play(filename, volume = 100, pitch = 100, start = 0)
- if MCI_DEFAULT
- mixer_play('BGS', filename, volume, pitch, true, start)
- elsif RPG_VERSION == 2
- mci_bgs_play(filename, volume, pitch, start)
- else
- mci_bgs_play(filename, volume, pitch)
- end
- end
- #---------------------------------------------------------------------------
- # * Use MCI Player play function
- #---------------------------------------------------------------------------
- alias mci_me_play me_play
- def me_play(filename, volume = 100, pitch = 100, start = 0)
- if MCI_DEFAULT
- mixer_play('ME', filename, volume, pitch, false, start)
- elsif RPG_VERSION == 2
- mci_me_play(filename, volume, pitch, start)
- else
- mci_me_play(filename, volume, pitch)
- end
- end
- #---------------------------------------------------------------------------
- # * Use MCI Player play function
- #---------------------------------------------------------------------------
- alias mci_se_play se_play
- def se_play(filename, volume = 100, pitch = 100, start = 0)
- if MCI_DEFAULT
- mixer_play('SE', filename, volume, pitch, false, start)
- elsif RPG_VERSION == 2
- mci_se_play(filename, volume, pitch, start)
- else
- mci_se_play(filename, volume, pitch, start)
- end
- end
- #---------------------------------------------------------------------------
- # Use MCI Player to play a file on a mixer using given parameters
- #---------------------------------------------------------------------------
- def mixer_play(mixer_name, filename, volume, pitch, repeat, start = 0)
- if ['BGM', 'BGS'].include?(mixer_name)
- return if self[mixer_name].filename == filename
- end
- self[mixer_name].play(filename, volume, pitch, repeat, start)
- end
- #---------------------------------------------------------------------------
- # * Use MCI Player stop
- #---------------------------------------------------------------------------
- alias mci_bgm_stop bgm_stop
- def bgm_stop
- MCI_DEFAULT ? @mixers['BGM'].stop : mci_bgm_stop
- end
- #---------------------------------------------------------------------------
- # * Use MCI Player stop
- #---------------------------------------------------------------------------
- alias mci_bgs_stop bgs_stop
- def bgs_stop
- MCI_DEFAULT ? @mixers['BGS'].stop : mci_bgs_stop
- end
- #---------------------------------------------------------------------------
- # * Use MCI Player stop
- #---------------------------------------------------------------------------
- alias mci_me_stop me_stop
- def me_stop
- MCI_DEFAULT ? @mixers['ME'].stop : mci_me_stop
- end
- #---------------------------------------------------------------------------
- # * Use MCI Player stop
- #---------------------------------------------------------------------------
- alias mci_se_stop se_stop
- def se_stop
- MCI_DEFAULT ? @mixers['SE'].stop : mci_se_stop
- end
- #---------------------------------------------------------------------------
- # * Use MCI Player fade
- #---------------------------------------------------------------------------
- alias mci_bgm_fade bgm_fade
- def bgm_fade(time)
- rate = RPG_VERSION == 0 ? 40 : 60
- MCI_DEFAULT ? @mixers['BGM'].fade((time / 1000) * rate) :
- mci_bgm_fade(time)
- end
- #---------------------------------------------------------------------------
- # * Use MCI Player fade
- #---------------------------------------------------------------------------
- alias mci_bgs_fade bgs_fade
- def bgs_fade(time)
- rate = RPG_VERSION == 0 ? 40 : 60
- MCI_DEFAULT ? @mixers['BGS'].fade((time / 1000) * rate) :
- mci_bgs_fade(time)
- end
- #---------------------------------------------------------------------------
- # * Use MCI Player fade
- #---------------------------------------------------------------------------
- alias mci_me_fade me_fade
- def me_fade(time)
- rate = RPG_VERSION == 0 ? 40 : 60
- MCI_DEFAULT ? @mixers['ME'].fade((time / 1000) * rate) :
- mci_me_fade(time)
- end
- end
- #-----------------------------------------------------------------------------
- # * Gives hash-type access of the mixers of the module
- #-----------------------------------------------------------------------------
- def self.[](mixer_name)
- unless @mixers.has_key?(mixer_name)
- @mixers[mixer_name] = Mixer.new(mixer_name)
- end
- return @mixers[mixer_name]
- end
- #-----------------------------------------------------------------------------
- # * Frame update
- #-----------------------------------------------------------------------------
- def self.update
- @mixers.each_value {|mixer| mixer.update }
- end
- #-----------------------------------------------------------------------------
- # * Simplified method for making calls directly to the Media Command Interface
- #-----------------------------------------------------------------------------
- def self.mci_eval(command)
- data = "\0" * 256
- MCISendString.call(command, data, 256, 0)
- return data.delete("\0")
- end
- #-----------------------------------------------------------------------------
- # * Iterator for the Audio mixers
- #-----------------------------------------------------------------------------
- def self.each_mixer
- @mixers.each_value {|mixer| yield mixer }
- end
- #-----------------------------------------------------------------------------
- # * Object initialization
- #-----------------------------------------------------------------------------
- def self.init
- if @mixers != nil
- # Don't remove this, it prevents memory leaks when F12 us used to restart
- MCISendString.call('close all', nil, 0, 0)
- end
- @mixers = {}
- ['BGM', 'BGS', 'ME', 'SE'].each {|name| @mixers[name] = Mixer.new(name) }
- end
- end
- #===============================================================================
- # ** Graphics
- #-------------------------------------------------------------------------------
- # Syncs the audio update used for fade effects with the frame update
- #===============================================================================
- module Graphics
- class << self
- alias mci_player_update update
- def update
- mci_player_update
- Audio.update
- end
- end
- end
- #===============================================================================
- # ** RTP
- #-------------------------------------------------------------------------------
- # Provides functions for getting the games RTP path(s) and files
- #===============================================================================
- module RTP
- # RMXP
- if Audio::RPG_VERSION == 0
- SUBFOLDERS = [
- 'Graphics/Animations', 'Graphics/Autotiles', 'Graphics/Battlebacks',
- 'Graphics/Battlers', 'Graphics/Characters', 'Graphics/Fogs',
- 'Graphics/Gameovers', 'Graphics/Icons', 'Graphics/Panoramas',
- 'Graphics/Pictures', 'Graphics/Tilesets', 'Graphics/Titles',
- 'Graphics/Transitions', 'Graphics/Windowskins', 'Audio/BGM',
- 'Audio/BGS', 'Audio/ME', 'Audio/SE'
- ]
- # RMVX
- elsif Audio::RPG_VERSION == 1
- SUBFOLDERS = [
- 'Graphics/Animations', 'Graphics/Battlers', 'Graphics/Characters',
- 'Graphics/Faces', 'Graphics/Parallaxes', 'Graphics/Pictures',
- 'Graphics/System', 'Audio/BGM', 'Audio/BGS', 'Audio/ME', 'Audio/SE'
- ]
- # RMVXA
- elsif Audio::RPG_VERSION == 2
- SUBFOLDERS = [
- 'Graphics/Animations', 'Graphics/Battlers', 'Graphics/Characters',
- 'Graphics/Faces', 'Graphics/Parallaxes', 'Graphics/Pictures',
- 'Graphics/System', 'Audio/BGM', 'Audio/BGS', 'Audio/ME', 'Audio/SE'
- ]
- end
- #-----------------------------------------------------------------------------
- # * Object initialization
- #-----------------------------------------------------------------------------
- def self.init
- @ini = Win32API.new('kernel32', 'GetPrivateProfileStringA', 'PPPPLP', 'L')
- @library = "\0" * 256
- @ini.call('Game', 'Library', '', @library, 256, '.\\Game.ini')
- @library.delete!("\0")
- @rtp_path = Win32API.new(@library, 'RGSSGetRTPPath', 'L', 'L')
- @path_with_rtp = Win32API.new(@library, 'RGSSGetPathWithRTP', 'L', 'P')
- @directories = {}
- SUBFOLDERS.each {|folder| @directories[folder] = entries(folder) }
- @initialized = true
- end
- #-----------------------------------------------------------------------------
- # * Returns an array of the full paths of all the game's installed RTPs
- #-----------------------------------------------------------------------------
- def self.paths
- paths = [1, 2, 3].collect {|id| @path_with_rtp.call(@rtp_path.call(id)) }
- paths = paths.find_all {|path| path != '' }
- # This is kind of a crappy way of doing this until the RMVX call works...
- common = File.join(ENV['CommonProgramFiles'], 'Enterbrain')
- common = case Audio::RPG_VERSION
- when 0 then File.join(common, 'RGSS', 'Standard')
- when 1 then File.join(common, 'RGSS2', 'RPGVX')
- when 2 then File.join(common, 'RGSS3', 'RPGVXAce')
- end
- if !paths.include?(common) && File.directory?(common)
- paths.push(common)
- end
- return paths
- end
- #-----------------------------------------------------------------------------
- # * Gives hash-like access to the RTP subfolders
- #-----------------------------------------------------------------------------
- def self.[](folder)
- return subfolder?(folder) ? @directories[folder] : []
- end
- #-----------------------------------------------------------------------------
- # * Returns true/false if the given subfolder exists
- #-----------------------------------------------------------------------------
- def self.subfolder?(folder)
- return @directories.has_key?(folder)
- end
- #-----------------------------------------------------------------------------
- # * Get a complete list of full paths of files found in the given subfolder
- # subfolder : The RTP folder whose files you want to get
- #-----------------------------------------------------------------------------
- def self.entries(subfolder)
- files = []
- paths.each {|path|
- dir = path + '\\' + subfolder
- if File.directory?(dir)
- files = (Dir.entries(dir) - ['.', '..']).collect {|f| dir + '\\' + f }
- end
- }
- return files
- end
- end
- #===============================================================================
- # ** Game_System
- #===============================================================================
- class Game_System
- #-----------------------------------------------------------------------------
- # * Public Instance Variables
- #-----------------------------------------------------------------------------
- attr_accessor :memorized_audio
- #-----------------------------------------------------------------------------
- # * Memorize Audio
- # channels : Names of channels, or omit argument to memorize all channels
- #-----------------------------------------------------------------------------
- def memorize_audio(*channels)
- @audio_memory = {}
- if channels.empty?
- Audio.each_mixer {|m|
- data = [m.filepath, m.volume, m.speed, m.repeat, m.position]
- @audio_memory[m.name] = data
- }
- else
- channels.each {|channel|
- m = Audio[channel]
- data = [m.filepath, m.volume, m.speed, m.repeat, m.position]
- @audio_memory[channel] = data
- }
- end
- end
- #-----------------------------------------------------------------------------
- # * Restore Audio
- # channels : Names of channels, or omit argument to restore all channels
- #-----------------------------------------------------------------------------
- def restore_audio(*channels)
- unless @audio_memory == nil || @audio_memory.empty?
- keys = channels.empty? ? @audio_memory.keys : channels
- keys.each {|name|
- data = @audio_memory[name]
- if data != nil && data[0] != nil
- Audio[name].play(data[0], data[1], data[2], data[3])
- Audio[name].seek(data[4])
- @audio_memory.delete(name)
- end
- }
- end
- end
- end
- # Intialize the MCI Player
- RTP.init
- Audio.init
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement