Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Messages - ForeverZer0

1
Holy hell, I have absolutely no recollection of every created that. I will look in my dropbox and fix link. Never fixed them after Dropbox changed all their crap around.


EDIT:

Oi! I actually found it.

https://drive.google.com/open?id=1xxUCKMm89lZRzGhWSy5EQfiqlfwFh6za
2
New Projects / Re: [Ruby] FMOD Low-Level API Wrapper
March 23, 2018, 02:59:51 pm
Quote from: Blizzard on March 23, 2018, 02:39:16 pm
Nice. If now somebody could pull the samples out of XP, we'd finally have a worthy MIDI audio replacement. xD


Added a generic Audio module as an example to get started with. Even in its basic form, already far more capable than RMXP.

  • Backwards compatible

  • Tempo changing

  • Adding DSP effects

  • Frequency changing

  • Pause, seek, etc. functions



I did not yet implement checking if sound is disabled via the properties of RMXP. I can add that snippet as well soon, as it is little complicated reading the registry keys.
3
New Projects / Re: [Ruby] FMOD Low-Level API Wrapper
March 23, 2018, 01:06:15 pm
MIDI control is native to FMOD, I am not 100% sure if uses its own, or just chooses based on the platform. Considering FMOD is used on about every kind of hardware and video game console out there, I imagine it probably uses its own for consistency. The documentation is a little light on the actual internal codecs, etc. and how they work.

It does have great MIDI support built-in, though, per MIDI channel gain, sound fonts, etc.

These methods in the Sound class deal with MIDI volumes, etc.
Spoiler: ShowHide
    ##
    # Retrieves the volume of a MOD/S3M/XM/IT/MIDI music channel volume.
    # @param index [Integer] MOD/S3M/XM/IT/MIDI music sub-channel to retrieve
    #   the volume for.
    # @return [Float] The volume of the channel from +0.0+ to +1.0+.
    #   Default = +1.0+.
    def music_volume(index)
      return 0 if index < 0 || index >= music_channel_count
      volume = FMOD.ptr
      FMOD.invoke(:Sound_GetMusicChannelVolume, handle, index, volume )
      volume.unpack('f').first
    end

    ##
    # Sets the volume of a MOD/S3M/XM/IT/MIDI music channel volume.
    # @param index [Integer] MOD/S3M/XM/IT/MIDI music sub-channel to set a
    #   linear volume for.
    # @param volume [Float] Volume of the channel from +0.0+ to +1.0+.
    #   Default = +1.0+.
    # @return [void]
    def set_music_volume(index, volume)
      return if index < 0 || index >= music_channel_count
      volume = volume.to_f.clamp(0.0, 1.0).to_win32
      FMOD.invoke(:Sound_SetMusicChannelVolume, index, volume)
    end
4
New Projects / Re: [Ruby] FMOD Low-Level API Wrapper
March 22, 2018, 05:24:18 pm
I am not really pleased with the structs or enum implementation.

For structs, inheriting from Struct.new(:whatever) is a little bit of a no-no, since you are also creating an anonymous class that never gets used. I used it as a bit of shortcut to writing all the attr_accessors and initializers manually, but it is not really the best practice to do that.

As far as the Enum class goes, I had a perfect idea to implement that better, using simple symbols or integers, without the need of enclosing it within the parent enum class, but now that it is so far integrated, it is going to be a painful change to alter all of the methods that use en enum, which is a few hundred.

And still more documentation with examples. I have spent far more time documenting than even writing code, getting really sick of it, lol.
5
New Projects / [Ruby] FMOD Low-Level API Wrapper
March 22, 2018, 04:52:32 pm
Introduction
I was in need of a good audio API when working on a C# application, and was unable to find an existing library that fit my needs, so I ended up making my own using the FMOD Low-Level API (the predecessor to the legacy FmodEx). After working with FMOD, and learning the ins-and-outs of how it worked, I saw the potential for making a Ruby port as well. Searching online turned up very little as far as any type of wrapper that could ever be considered close to complete, and only had the basic functionality of FMOD implemented. This including existing RPG Maker scripts that wrapped the basic functionality of the old FmodEx, but nothing more. This prompted me to write my own wrapper.

Being my programming experience begin with Chaos Project and old Ruby 1.8.2, I decided to write in a way that would remain compatible with old versions of Ruby, and foregoing using any Ruby gems or functionality that didn't exist in Ruby 1.8.2, and decided to stick with using Win32API to wrap the FMOD functions, and didn't allow myself the convenience of using a new library like Fiddle of FFI to make things simple.

Currently, the project is approaching the alpha stage. Nearly ALL core functions are implemented, but there has been no in-depth testing yet, and there are still a few mostly uncommon functionalities that need worked on. I have spent A LOT of time making documentation for it, as it is a huge API that definitely has a learning curve. If you don't believe me, see the spoiler.

Spoiler: ShowHide


In the future, I will be releasing this as a gem, but as it stands now, the project can be loaded into RPG Maker with a simple "require", and built upon that. I may even create a basic Audio module rewrite with this codebase in the future, but if anyone else feels like doing it, feel free to check out the code and build upon it for RPG Maker Audio module.



Features

  • Support for 20+ audio formats "out of the box", including all the RPG Maker standards (.mp3, .ogg, .mid, .wma, ect.) with support for more via a plugin system using existing FMOD plugins libraries

  • All basic playback control (play, pause, resume, seek, volume, etc, etc.) that you would expect of any audio player.

  • 20+ built-in effects for sound, including pitch shifting, flange, advanced reverb environments, multi-band parametric equalizer, chorus, tremolo, oscillator,  and many, many more.

  • Streaming of all audio formats, not just MP3

  • Full 3D and 2D support, with distance/directional based sound, each with own reverb environment.

  • Support for custom speaker setups, full surround sound support for up to 12 speakers.

  • Driver selection.

  • Supports music tags (ID3, etc) if that's your thing...

  • Much, much more.



As I stated above, this project is still in its infancy, but already far surpasses any existing audio API that I could find for the Ruby language, and because I love you all, I kept it compatible with RPG Maker XP and above.

This post is meant to a preview for a full release, and I would recommend not to port it as it currently is in a completed project. As so much yet is untested to any passable standard, lacks complete protection against segmentation faults, etc.



Download

Scipts

FMOD DLL (Required)

Documentation

Source - Github



Getting Started

Download the scripts, place unzipped directory in RPG Maker project folder along with the downloaded FMOD dll.
Open script editor, add the line:
require File.expand_path('lib/fmod')


Just to give you idea to get started, snippet to play a sound:
sys = FMOD::System.create
sound = sys.create_sound('My Audio File.mp3')
sys.play_sound(sound)


Things to remember, the documentation, which is posted above, is currently lacking a lot of examples, so feel free to download the help file from FMOD's website, which can still be helpful on how to do things until the examples are created.

The documentation is not compiled, but can simply be unzipped and opening the "index" file within, which will open it in your browser.

FMOD object's need disposed! Once you are done using a sound, system, etc, call dispose or release on it to free the underlying handle. Obviously this behavior would be taken care of automatically when ported to RPG Maker. This is a developer's API, not an end-user product.

Below is generic audio module I wrote real quick just as an example as an example. It is already far more capable than the built-in Audio, but really more intended as just boiler-plate code.
Audio Module: ShowHide
$:.unshift(File.join(File.dirname(__FILE__), 'lib'))

require 'fmod'

# Undefine the old Audio module so there is no interference.
if defined? Audio
  Object.send(:remove_const, :Audio)
end

##
# Complete redefinition of the Audio module with additional capabilities.
#
# @note <b>Notes on Pitch</b>
#
#   By default, pitch is changed by simply altering the speed of the sound. If
#   you wish to accomplish true pitch shifting, use the PitchShifter DSP unit,
#   which alters pitch without changing playback speed.
# @author Eric "ForeverZer0" Freed
module Audio

  ##
  # Only change if you have a project that renames the "Game.exe" and "Game.ini"
  EXE = 'Game'

  ##
  # File extensions that will be included for searching audio paths.
  # For performance reasons, only include extensions that your project uses.
  EXTENSIONS = %w[.mp3 .ogg .mid .wma]

  ##
  # Channel names, as symbols. Removing the default names listed here will break
  # backwards compatibility.
  CHANNELS = [:BGM, :BGS, :ME, :SE].freeze

  include FMOD::Enums
  include FMOD::Effects

  ##
  # Retrieves a hash using the symbols located in {CHANNELS} mapping to each
  # channel handle.
  # @return [Hash<Symbol, FMOD::Channel>] The {FMOD::Channel} instances.
  def self.channels
    @channels
  end

  ##
  # Begins playback on the specified channel.
  #
  # File extensions may be omitted. All paths should be relative to the base
  # project directory. RTP directories will also be searched if file is not
  # found locally.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param filename [String] Relative filename to play. File extensions may be
  #   omitted.
  # @param volume [Integer] Volume level. By default this within range of +0+
  #   to +100+, but FMOD supports amplification and higher values.
  # @param pitch [Integer] Pitch level. +50+ to +200+.
  # @param looping [Boolean] +true+ to have sound loop; otherwise +false+.
  # @param effects [FMOD::DSP] Optional DSP effects to apply to the sound. This
  #   is more efficient than adding later.
  # @return [FMOD::Channel] The channel instance that is playing the sound.
  def self.play(channel, filename, volume, pitch, looping, *effects)
    if channel_playing?(channel, filename)
      @channels[channel].volume = volume * 0.01
      @channels[channel].pitch = pitch * 0.01
      return
    end
    @sounds[channel].release unless @sounds[channel].nil?
    mode = looping ? MODE[:loop_normal] : MODE[:default]
    @sounds[channel] = @fmod.create_stream(full_path(filename), mode)
    @channels[channel] = @fmod.play_sound(@sounds[channel], nil, true)
    @channels[channel].volume = volume * 0.01
    @channels[channel].pitch = pitch * 0.01
    effects.reverse.each { |dsp| @channels[channel].add_dsp 0, dsp }
    @channels[channel].resume
  end

  ##
  # Creates a DSP unit that can be applied to a channel to alter sound.
  #
  # Optionally applies it to channels if specified.
  # @param type [Integer, Class] Either an integer value specified in the
  #   {DSP_TYPE} enumeration, or a class that is defined in the {FMOD::Effects}
  #   namespace.
  # @param channels [Symbol] Any number of channel names defined in {CHANNELS}
  #   to apply the effect to.
  # @return [FMOD::DSP] The created DSP effect.
  def self.create_effect(type, *channels)
    @effects << @fmod.create_dsp(type)
    channels.each { |channel| add_effect(channel, @effects.last) }
    @effects.last
  end

  ##
  # Adds an existing DSP effect to a channel.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param effect [FMOD::DSP] AN existing DSP effect.
  # @return [void]
  def self.add_effect(channel, effect)
    return unless channel_valid?(channel) && effect.is_a?(FMOD::DSP)
    @channels[channel].add_dsp(0, effect)
  end

  ##
  # Removes an existing DSP effect from a channel.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param effect [FMOD::DSP, Integer] Either the DSP effect instance to remove,
  #   or the index into the channel's DSP chain to remove.
  # @return [void]
  def self.remove_effect(channel, effect)
    return unless channel_valid?(channel)
    @channels[channel].remove_dsp(effect)
  end

  ##
  # Retrieves the loaded effect on the channel at the given index.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param index [Integer] Index into the channel's DSP chain to retrieve the
  #   desired effect.
  # @return [FMOD::DSP, nil] The desired DSP effect, or +nil+ if channel or
  #   index was invalid.
  def self.effect(channel, index)
    return unless channel_valid?(channel)
    @channels[channel][index]
  end

  ##
  # Returns an array of created DSP effects.
  # @param channel [Symbol] If specified, returns only effects currently applied
  #   to the channel.
  # @return [Array<FMOD::DSP>] Array of all effects created by the user.
  def self.effects(channel = nil)
    return @effects if channel.nil?
    return unless channel_valid?(channel)
    @channels[channel].dsps
  end

  ##
  # Retrieves the current volume of a sound.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @return [Integer] The current volume, a value typically between 0 and 100.
  def self.volume(channel)
    channel_valid?(channel) ? (@channels[channel].volume * 100).to_i : 0
  end

  ##
  # Changes the volume of a channel.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param volume [Integer] The volume to set, typically a value between 0 and
  #   100, though amplification is supported.
  # @return [Channel, nil] The channel object if channel was valid; otherwise
  #   +nil+.
  def self.change_volume(channel, volume)
    return unless channel_valid?(channel)
    @channels[channel].volume  = volume * 0.01
    @channels[channel]
  end

  ##
  # Retrieves the current pitch of a sound.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @return [Integer] The current pitch, a value between 50 and 200.
  def self.pitch(channel)
    channel_valid?(channel) ? (@channels[channel].pitch * 100).to_i : 100
  end

  ##
  # Changes the pitch of a channel.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param pitch [Integer] The pitch to set, a value between 50 (one octave
  #   down) and 200 (one octave up).
  # @note Out of range values will be clamped.
  # @return [Channel, nil] The channel object if channel was valid; otherwise
  #   +nil+.
  def self.change_pitch(channel, pitch)
    return unless channel_valid?(channel)
    @channels[channel].pitch = pitch * 0.01
    @channels[channel]
  end

  ##
  # Retrieves the current playback position of the sound.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @return [Integer] The current position, in milliseconds.
  def self.position(channel)
    channel_valid?(channel) ? @channels[channel].position : 0
  end

  ##
  # Moves the playback position to the specified location in the sound.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param ms [Integer] The playback position, in milliseconds, to set.
  # @return [Channel, nil] The channel object if channel was valid; otherwise
  #   +nil+.
  def self.seek(channel, ms)
    return unless channel_valid?(channel)
    @channels[channel].seek(ms)
  end

  ##
  # Stops playback on the specified channel.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @return [Channel, nil] The channel object if channel was valid; otherwise
  #   +nil+.
  def self.stop(channel)
    return unless channel_valid?(channel)
    @channels[channel].stop
  end

  ##
  # Pauses playback on the specified channel.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @return [Channel, nil] The channel object if channel was valid; otherwise
  #   +nil+.
  def self.pause(channel)
    return unless channel_valid?(channel)
    @channels[channel].pause
  end

  ##
  # Resumes a previously paused channel, or does nothing if channel is invalid
  # or not paused.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @return [Channel, nil] The channel object if channel was valid; otherwise
  #   +nil+.
  def self.resume(channel)
    return unless channel_valid?(channel)
    @channels[channel].resume
  end

  ##
  # This is same as "time-stretching", changing the speed of playback without
  # effecting the pitch of the sound.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param speed [Float] A values between 0.5 (half speed) and 2.0 (double
  #   speed).
  # @return [void]
  def self.tempo(channel, speed)
    return unless channel_valid?(channel)
    speed = [0.5, speed, 2.0].sort[1]
    pitch_shifter = @channels[channel].dsps.find { |dsp| dsp.type == 13 }
    if pitch_shifter.nil?
      pitch_shifter = create_effect(13, channel)
      pitch_shifter.fft_size = 4096
    end
    @channels[channel].pitch = speed
    pitch_shifter.pitch = 1.0 / speed
  end

  ##
  # Changes the playback speed of a sound. Altering the speed also changed the
  # pitch.
  #
  # Negative values are accepted to play a sound backwards.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param hz [Numeric] The frequency, in Hz, to play the channel at. Default
  #   speed is +44100+ Hz, so +-44100+ is normal speed backwards, +22050+ is
  #   half speed, +88200+ is double, etc.
  def self.frequency(channel, hz)
    return unless channel_valid?(channel)
    @channels[channel].frequency = hz
  end

  ##
  # Transitions volume on the specified channel to the a target volume of the
  # given number of milliseconds.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param time [Integer] Length of time in milliseconds the transition will
  #   occur over.
  # @param target_volume [Integer] The target volume to transition to, typically
  #   a value between 0 and 100, though amplification is supported.
  # @note When volume is transitioned to +0+, the channel will be stopped when
  #   after reaching that volume.
  def self.fade(channel, time, target_volume = 0)
    return unless channel_valid?(channel)
    volume = @channels[channel].volume - (target_volume * 0.01)
    frames = time / Graphics.frame_rate
    increment = volume / frames
    @fade_points.delete_if { |fade| fade[0] == channel }
    @fade_points.push [channel, frames, increment, target_volume]
  end

  ##
  # Checks if the specified channel is still valid and references an existing
  # FMOD Channel. FMOD reuses channels automatically and can "steal" them for
  # higher priority sounds when necessary, or more commonly after a sound has
  # completed playing and the channel is stopped.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @return [Boolean] +true+ if channel is still valid; otherwise +false+.
  def self.channel_valid?(channel)
    return false if @channels[channel].nil?
    @channels[channel].valid?
  end

  ##
  # Checks if the given channel is currently playing. If a filename is
  # specified, additionally checks if that is the file being played.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param filename [String, nil] A filename to check if is playing on the
  #   channel, or +nil+ to ignore.
  # @return [Boolean] Returns +true+ if the channel is playing, and additionally
  #   if the filename is playing if specified; otherwise +false+.
  def self.channel_playing?(channel, filename = nil)
    return false unless channel_valid?(channel)
    if @channels[channel].playing?
      name = @channels[channel].sound.name
      return File.basename(name, File.extname(name)) == File.basename(filename)
    end
    false
  end

  ##
  # Initializes the Audio module.
  # @api private
  # @return [void]
  def self.initialize
    return if defined? @fmod
    @fmod = FMOD::System.create
    build_paths
    @found = {}
    @sounds = {}
    @channels = {}
    @effects = []
    @fade_points = []
  end

  ##
  # Updates the FMOD system object and fading.
  # @note This is called automatically each frame with each Graphics update,
  #   there is no need to call this method manually.
  # @api private
  # @return [void]
  def self.update
    @fmod.update
    @fade_points.delete_if do |fade|
      if channel_valid?(fade[0])
        @channels[fade[0]].volume -= fade[2]
        fade[1] -= 1
        if fade[1] <= 0
          @channels[fade[0]].stop if fade[3] == 0
          return true
        end
        return false
      end
      true
    end
  end

  ##
  # Builds the search paths that will be used for searching audio files,
  # including RTP paths defined in the projects .ini file.
  # @return [void]
  # @api private
  def self.build_paths
    ini = Win32API.new('kernel32','GetPrivateProfileString','pppplp','l')
    ini.call(EXE, 'Library', '', lib = "\0" * 256, 256, "./#{EXE}.ini")
    lib.delete!("\0")
    rtp_path = Win32API.new(lib, 'RGSSGetRTPPath', 'l', 'l')
    path_with_rtp = Win32API.new(lib, 'RGSSGetPathWithRTP', 'l', 'p')
    @search_paths = [Dir.getwd]
    (1..3).each do |i|
      id = rtp_path.call(i)
      rtp = path_with_rtp.call(id)
      next if rtp == ''
      @search_paths << rtp.gsub('\\', '/')
    end
  end

  ##
  # Returns the full path from a relative path. File extension may be omitted,
  # in which case any matching file that has an extension in the {EXTENSIONS}
  # setting will be retrieved.
  #
  # Searches locally first, and RTP directories if not found.
  # @param relative_path [String] A filename relative to the base directory.
  # @api private
  # @return [String] The full path to the file, including found extension.
  # @raise [Errno::ENOENT] Raised when a matching file cannot be found.
  # @note Once found, filenames are cached to reduce overhead on repeated calls.
  def self.full_path(relative_path)
    key = relative_path.to_sym
    if @found.has_key?(key)
      return @found[key]
    end
    catch :found do
      @search_paths.each do |dir|
        if File.extname(relative_path) != ''
          filename = File.join(dir, relative_path)
          next unless FileTest.exists?(filename)
          @found[key] = filename
          break
        else
          EXTENSIONS.each do |ext|
            filename = File.join(dir, relative_path + ext)
            next unless FileTest.exists?(filename)
            @found[key] = filename
            throw :found
          end
        end
      end
    end
    # noinspection RubyResolve
    raise Errno::ENOENT, relative_path unless @found.has_key?(key)
    @found[key]
  end

  ##
  # @deprecated Use {Audio.play}. Backwards compatibility for existing Audio.
  # Starts BGM playback. Sets the file name, volume, and pitch in turn.
  #
  # Also automatically searches files included in RGSS-RTP.
  # @param filename [String] Relative filename to play. File extensions may be
  #   omitted.
  # @param volume [Integer] Volume level. By default this within range of +0+
  #   to +100+, but FMOD supports amplification and higher values.
  # @param pitch [Integer] Pitch level. +50+ to +200+.
  # @return [void]
  def self.bgm_play(filename, volume = 100, pitch = 100)
    play(:BGM, filename, volume, pitch, true)
  end

  ##
  # @deprecated Use {Audio.play}. Backwards compatibility for existing Audio.
  # Starts BGS playback. Sets the file name, volume, and pitch in turn.
  #
  # Also automatically searches files included in RGSS-RTP.
  # @param filename [String] Relative filename to play. File extensions may be
  #   omitted.
  # @param volume [Integer] Volume level. By default this within range of +0+
  #   to +100+, but FMOD supports amplification and higher values.
  # @param pitch [Integer] Pitch level. +50+ to +200+.
  # @return [void]
  def self.bgs_play(filename, volume = 100, pitch = 100)
    play(:BGS, filename, volume, pitch, true)
  end

  ##
  # @deprecated Use {Audio.play}. Backwards compatibility for existing Audio.
  # Starts ME playback. Sets the file name, volume, and pitch in turn.
  #
  # Also automatically searches files included in RGSS-RTP.
  # @param filename [String] Relative filename to play. File extensions may be
  #   omitted.
  # @param volume [Integer] Volume level. By default this within range of +0+
  #   to +100+, but FMOD supports amplification and higher values.
  # @param pitch [Integer] Pitch level. +50+ to +200+.
  # @return [void]
  def self.me_play(filename, volume = 100, pitch = 100)
    play(:ME, filename, volume, pitch, false)
  end

  ##
  # @deprecated Use {Audio.play}. Backwards compatibility for existing Audio.
  # Starts SE playback. Sets the file name, volume, and pitch in turn.
  #
  # Also automatically searches files included in RGSS-RTP.
  # @param filename [String] Relative filename to play. File extensions may be
  #   omitted.
  # @param volume [Integer] Volume level. By default this within range of +0+
  #   to +100+, but FMOD supports amplification and higher values.
  # @param pitch [Integer] Pitch level. +50+ to +200+.
  # @return [void]
  def self.se_play(filename, volume = 100, pitch = 100)
    play(:SE, filename, volume, pitch, false)
  end

  ##
  # @deprecated Use {Audio.stop}. Backwards compatibility for existing Audio.
  # Stops BGM playback.
  def self.bgm_stop
    stop(:BGM)
  end

  ##
  # @deprecated Use {Audio.stop}. Backwards compatibility for existing Audio.
  # Stops BGS playback.
  def self.bgs_stop
    stop(:BGS)
  end

  ##
  # @deprecated Use {Audio.stop}. Backwards compatibility for existing Audio.
  # Stops ME playback.
  def self.me_stop
    stop(:ME)
  end

  ##
  # @deprecated Use {Audio.stop}. Backwards compatibility for existing Audio.
  # Stops SE playback.
  def self.se_stop
    stop(:SE)
  end

  ##
  # @deprecated Use {Audio.fade}. Backwards compatibility for existing Audio.
  # Transitions the BGM volume (up or down) to the target volume over the
  # specified interval.
  # @param time [Integer] The time in milliseconds for the transition to be
  #   applied. The volume will be adjusted on a per-frame basis.
  # @return [void]
  def self.bgm_fade(time, target_volume = 0)
    fade(:BGM, time, target_volume)
  end

  ##
  # @deprecated Use {Audio.fade}. Backwards compatibility for existing Audio.
  # Transitions the BGS volume (up or down) to the target volume over the
  # specified interval.
  # @param time [Integer] The time in milliseconds for the transition to be
  #   applied. The volume will be adjusted on a per-frame basis.
  # @return [void]
  def self.bgs_fade(time, target_volume = 0)
    fade(:BGS, time, target_volume)
  end

  ##
  # @deprecated Use {Audio.fade}. Backwards compatibility for existing Audio.
  # Transitions the ME volume (up or down) to the target volume over the
  # specified interval.
  # @param time [Integer] The time in milliseconds for the transition to be
  #   applied. The volume will be adjusted on a per-frame basis.
  # @return [void]
  def self.me_fade(time, target_volume = 0)
    fade(:ME, time, target_volume)
  end

  ##
  # @deprecated Use {Audio.fade}. Backwards compatibility for existing Audio.
  # Transitions the SE volume (up or down) to the target volume over the
  # specified interval.
  # @param time [Integer] The time in milliseconds for the transition to be
  #   applied. The volume will be adjusted on a per-frame basis.
  # @return [void]
  def self.se_fade(time, target_volume = 0)
    fade(:SE, time, target_volume)
  end

  ##
  # Disposes the Audio module, releasing handles to the underlying objects.
  # @return [void]
  # @note It is strongly recommended not to alter or interfere with this call.
  # @api private
  def self.finalize
    @channels.each_value { |channel| channel.stop if channel.valid? }
    @effects.each { |dsp| dsp.release }
    @sounds.each_value { |sound| sound.release }
    @fmod.release
  end

  private_class_method :build_paths
  private_class_method :full_path
  private_class_method :finalize

  initialize
end

module Graphics

  class << self

    ##
    # Original update method for Graphics.
    # @return [void]
    alias update_audio update

    ##
    # Piggybacks the Graphics update of the Audio module. Updating is necessary
    # for fading and some other less common functions.
    # @return [void]
    def update
      Audio.update
      update_audio
    end
  end
end


module Kernel
  alias finalize_audio exit
  def exit(*args)
    begin
      Audio.send(:finalize)
    ensure
      finalize_audio(*args)
    end
  end
end








6
Script Requests / Re: Conditional Branch addon
March 22, 2018, 02:43:01 pm
Quote from: KK20 on March 22, 2018, 02:19:52 pm
@F0: If you're so opposed to it, then just combine the two as
$game_system.playing_bgm && $game_system.playing_bgm.name == "TRACKNAME"

There's nothing stating you can't use and/or in Conditional Branch scripts.


I am not opposed to anything, lol, and I am as guilty as anyone in the community of trying to make everything a "one-liner" back when I wrote RGSS scripts. Over the years I have obviously learned more as everyone has, and cringe at some of the stuff I wrote that I was clever at the time, and I am sure years from now I will feel the same of my current code as I learn more.

I just found the convention of trying to compact every little bit of code into the shortest possible number of characters/lines a bit odd,
even when at the cost of performance, which is typically the reasoning behind trying to make compact code. Some irony there.

This is not an issue unique to RGSS, but looking back, it does seem to hold some greater measure in the community that it is somehow "better" if two different snippets do the same thing, but one is shorter than the other.

Obviously this is a case-by-case basis, and there is no "rule" that doesn't have plenty of exceptions, but just stating an observation. The above example is a somewhat poor example, and would merely be a micro-optimization that holds no sway over the whole at all.

I am against enforcing any type of standard (I still remember SDK...), but I am for "good coding practice", even when it may be inconvenient for the situation. I wish I would have known things I know now about Ruby when I wrote scripts, but alas, that time has passed  :P

I am far, far from being masterful, but I have recently picked Ruby back up as a hobby, and realizing the error of my ways back then. I would suggest to anyone who is a performance fanatic to really delve deep into the Ruby C API, and really see whats going on behind the scenes with each line in your Ruby script.
7
Script Requests / Re: Conditional Branch addon
March 22, 2018, 01:59:33 pm
It is less efficient using the one line with a rescue as opposed to using two lines.

It goes against Ruby coding practice to use rescue for anything other than when it is needed, for catching exceptions, which should be "exceptional". A BGM playing or not I don't think is really that exceptional of an event that couldn't be handled other ways.

The reasoning is that "rescue" in Ruby is a somewhat expensive operation, as it it builds an entire stacktrace by iterating through the call stack that it would not normally be doing.

Back when I coded for RGSS, I was guilty of the community's strange convention that "shorter, compact code is always better", but in reality, it is not the case at all. I have spent a lot of time digging around the C side of Ruby the past few months, and realized that there are many, many things that are the standard convention in the RM community, but are actually bad practices, and are not even conventional in Ruby.

Probably should have split this topic off before blabbering on...

EDIT: I do realize that in this particular situation, it hardly matters, as I don't imagine it is invoked often enough to matter, and it is an unneeded micro-optimization, but I speak more on the global scale of simply better coding practices.
8
At a glance, looks like you could simply delete the branch for 8_DIR or whatever its called at the beginning of the jump method.
9
General Discussion / Re: See With Your Ears - Video
February 23, 2018, 12:10:17 am
Not long ago, I actually was dabbling back into Ruby and made a complete wrapper around the newest version of the FMOD low-level API, the modern version of the legacy FMODex. Well, not 100% complete, but complete as far as anything that would ever be used in a 2D game, including all the DSPs for effects, reverb, playback control, etc.

It was not specifically intended for RPG Maker, was built for compatibility for Ruby 1.9+, and relied heavily upon Fiddle for interop, which won't work with RM, but an older version of it before I upgraded used all Win32API calls. I only made the transition do to hitting a wall with delegates for callback functions, since Ruby has no mechanism for that in older versions, but everything else *should* be compatible. It's quite large, spread across a lot of .rb docs, but if anyone wants the old code to port over, they are welcome to it.
10
Welcome! / Re: Hello...
January 30, 2017, 02:46:14 pm
 :welcome:
12
Welcome! / Re: Time for some Alexis action!
November 21, 2016, 10:42:09 am
:welcome:
13
Welcome! / Re: Hi I'm Mip and I'm making a game
July 23, 2016, 04:56:37 pm
:welcome:
14
Welcome! / Re: Hellew o/
July 05, 2016, 07:09:10 am
:welcome:
15
A couple of years ago I made a dll that performs all sorts of manipulations on bitmaps. Never did complete it totally, and it is written in C# with .NET Framework, but much of the methods could be easily translated to another language. You can at least see examples of how the pointers to the bitmaps are handled, etc.

https://sourceforge.net/projects/rpgnet/
16
Welcome! / Re: I'm back
May 18, 2016, 08:44:13 pm
 :welcome:
17
Welcome! / Re: Hi all!! Starmage here!
May 18, 2016, 08:42:18 pm
 :welcome:
18
Welcome! / Re: I come in peace
May 16, 2016, 06:46:27 pm
 :welcome:
19
Welcome! / Re: Olá! :)
May 13, 2016, 11:52:09 pm
Portugal? Never heard of it.
20
You don't need to lock topics, they can remain open.