bass.rbプロトタイプ

ちょっとデカいが(゚ε゚)キニシナイ!!
bass.dllは、un4seenさまのサイト、
http://www.un4seen.com/
の一番上にダウンロードリンクがある。

#!ruby -Ks
# Rubyからbass.dllを使うラッパ bass.rb ver 0.0.1α
require 'Win32API'

module Bass
  # 構造体やポインタを扱うものは使い方に注意。
  # 定義が間違っている可能性もあるので使うときは確認すること。
  BASS_GetVersion = Win32API.new("bass", "BASS_GetVersion", "", "I")
  BASS_ErrorGetCode = Win32API.new("bass", "BASS_ErrorGetCode", "", "I")
  BASS_Init = Win32API.new("bass", "BASS_Init", "IIIIP", "I")
  BASS_Free = Win32API.new("bass", "BASS_Free", "", "I")
  BASS_Start = Win32API.new("bass", "BASS_Start", "", "I")
  BASS_Stop = Win32API.new("bass", "BASS_Stop", "", "I")
  BASS_Pause = Win32API.new("bass", "BASS_Pause", "", "I")
  BASS_SetVolume = Win32API.new("bass", "BASS_SetVolume", "I", "I")
  BASS_GetVolume = Win32API.new("bass", "BASS_GetVolume", "", "I")
  BASS_SampleLoad = Win32API.new("bass", "BASS_SampleLoad", "IPIIIII", "I")
  BASS_SampleFree = Win32API.new("bass", "BASS_SampleFree", "I", "I")
  BASS_SampleGetChannel = Win32API.new("bass", "BASS_SampleGetChannel", "II", "I")
  BASS_SampleStop = Win32API.new("bass", "BASS_SampleStop", "I", "I")
  BASS_StreamCreateFile = Win32API.new("bass", "BASS_StreamCreateFile", "IPIIIII", "I")
  BASS_StreamFree = Win32API.new("bass", "BASS_StreamFree", "I", "I")
  BASS_ChannelFlags = Win32API.new("bass", "BASS_ChannelFlags", "III", "I")
  BASS_ChannelPlay = Win32API.new("bass", "BASS_ChannelPlay", "II", "I")
  BASS_ChannelStop = Win32API.new("bass", "BASS_ChannelStop", "I", "I")
  BASS_ChannelPause = Win32API.new("bass", "BASS_ChannelPause", "I", "I")
  BASS_ChannelSetAttribute = Win32API.new("bass", "BASS_ChannelSetAttribute", "III", "I")
  BASS_ChannelSlideAttribute = Win32API.new("bass", "BASS_ChannelSlideAttribute", "IIII", "I")
  BASS_ChannelIsSliding = Win32API.new("bass", "BASS_ChannelIsSliding", "II", "I")

  # bass.dllのエラーコード
  Errmsg = {
    1=>"MEM",2=>"FILEOPEN",3=>"DRIVER",4=>"BUFLOST",5=>"HANDLE",6=>"FORMAT",7=>"POSITION",8=>"INIT",
    9=>"START",14=>"ALREADY",18=>"NOCHAN",19=>"ILLTYPE",20=>"ILLPARAM",21=>"NO3D",22=>"NOEAX",23=>"DEVICE",
    24=>"NOPLAY",25=>"FREQ",27=>"NOTFILE",29=>"NOHW",31=>"EMPTY",32=>"NONET",33=>"CREATE",34=>"NOFX",
    37=>"NOTAVAIL",38=>"DECODE",39=>"DX",40=>"TIMEOUT",41=>"FILEFORM",42=>"SPEAKER",43=>"VERSION",44=>"CODEC",
    45=>"ENDED",-1=>" UNKNOWN"
  }

  # bass.dll初期化
  def self.init(hWnd, samplerate = 44100)
    if (BASS_GetVersion.call >> 16) != 0x0204 then
      raise("bass.dllバージョン2.4系以外には対応しておりません")
    end
    if BASS_Init.call(-1, samplerate, 0, hWnd, nil) == 0 then
      raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
    end
  end

  # bass.dll解放
  def self.free
    if BASS_Free.call == 0 then
      raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
    end
  end

  # sampleロード
  def self.loadSample(filename, max = 1)
    return Sample.new(filename, max)
  end

  # streamロード
  def self.loadStream(filename)
    return Stream.new(filename)
  end

  # Sampleを表すクラス
  class Sample
    def initialize(filename, max = 1)
      @handle = BASS_SampleLoad.call(0, filename, 0, 0, 0, max, 0x20000)
      if @handle == 0 then
        raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
      end
    end

    # 解放
    def free
      if BASS_SampleFree.call(@handle) == 0 then
        raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
      end
    end

    # 再生
    def play(option = {})
      ch = BASS_SampleGetChannel.call(@handle, 0)
      if ch == 0 then
        raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
      end

      if option[:loop] then
        if BASS_ChannelFlags.call(ch, 4, 4) == -1 then
          raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
        end
      end
      if option[:pan] then
        if BASS_ChannelSetAttribute.call(ch, 3, [option[:pan]].pack("f").unpack("I")[0]) == -1 then
          raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
        end
      end
      if option[:volume] then
        if BASS_ChannelSetAttribute.call(ch, 2, [option[:volume]].pack("f").unpack("I")[0]) == -1 then
          raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
        end
      end

      if BASS_ChannelPlay.call(ch, 0) == 0 then
        raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
      end
      return ch
    end

    # 再生中のchのパン変更。-1.0(左)〜1.0(右)。float型で。
    def setPan(ch, pan)
      if BASS_ChannelSetAttribute.call(ch, 3, [pan].pack("f").unpack("I")[0]) == -1 then
        raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
      end
    end

    # 再生中のchのボリューム変更。0〜1まで、float型で。
    def setVolume(ch, v)
      if BASS_ChannelSetAttribute.call(ch, 2, [v].pack("f").unpack("I")[0]) == -1 then
        raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
      end
    end

    # ch停止
    def stop(ch = nil)
      if ch == nil then
        if BASS_SampleStop.call(@handle) == 0 then
          raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
        end
      else
        if BASS_ChannelStop.call(ch) == 0 then
          raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
        end
      end
    end
  end

  # Streamを表すクラス
  class Stream
    def initialize(filename)
      @ch = BASS_StreamCreateFile.call(0, filename, 0, 0, 0, 0, 0)
      if @ch == 0 then
        raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
      end
    end

    # 解放
    def free
      if BASS_StreamFree.call(@ch) == 0 then
        raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
      end
    end

    # 再生
    def play(option = {})
      if option[:loop] then
        if BASS_ChannelFlags.call(@ch, 4, 4) == -1 then
          raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
        end
      end
      if option[:pan] then
        if BASS_ChannelSetAttribute.call(@ch, 3, [option[:pan]].pack("f").unpack("I")[0]) == -1 then
          raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
        end
      end
      if option[:volume] then
        if BASS_ChannelSetAttribute.call(@ch, 2, [option[:volume]].pack("f").unpack("I")[0]) == -1 then
          raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
        end
      end
      if BASS_ChannelPlay.call(@ch, 0) == 0 then
        raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
      end
    end


    # パン変更。-1.0(左)〜1.0(右)。float型で。
    def pan=(pan)
      if BASS_ChannelSetAttribute.call(@ch, 3, [pan].pack("f").unpack("I")[0]) == -1 then
        raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
      end
    end

    # ボリューム変更。0〜1まで、float型で。
    def volume=(v)
      if BASS_ChannelSetAttribute.call(@ch, 2, [v].pack("f").unpack("I")[0]) == -1 then
        raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
      end
    end

    # 停止
    def stop
      if BASS_ChannelStop.call(@ch) == 0 then
        raise("BASS_ERROR_#{Errmsg[BASS_ErrorGetCode.call]}")
      end
    end
  end
end

#Stream再生とパン変更のサンプル 要DXRuby1.0.4
#require "dxruby"
#Bass.init(Window.hWnd)
#s = Bass.loadStream("test.ogg")
#s.play(:loop=>true)
#pan = 0
#
#Window.loop do
#  pan += Input.x * 0.02
#  pan = -1.0 if pan < -1.0
#  pan = 1.0 if pan > 1.0
#  s.pan=pan
#
#  break if Input.keyPush?(K_ESCAPE)
#end
#s.free
#Bass.free

#Sample再生のサンプル 要DXRuby1.0.4
#Sampleはデータを全てメモリ内に読み込んでデコードします。
#BGMにMP3やOGGを使う場合は、Streamをオススメします。
#Sampleはplayするとチャンネル番号が返ってきて、
#その番号を使って再生中の音のパンや音量を変更することができます。
#StremもSampleも、playの引数に:loop=>trueを指定するとループします。
#:panでパン設定、:volumeで音量設定もできます。
#require "dxruby"
#Bass.init(Window.hWnd)
#s = Bass.loadSample("test2.wav")
#ch = s.play(:loop=>true)
#pan = 0
#
#Window.loop do
#  pan += Input.x * 0.02
#  pan = -1.0 if pan < -1.0
#  pan = 1.0 if pan > 1.0
#  s.setPan(ch, pan)
#
#  break if Input.keyPush?(K_ESCAPE)
#end
#
#s.free
#Bass.free

#bassの各種機能については今後、気が向いたときに追加していきます。

とりあえずSampleとStreamの再生、ボリューム、パンの設定のみ。
DXRuby1.0.4が必要で、まだリリースしていないのだがそんなことは(゚ε゚)キニシナイ!!
動かしてみたい人はSVNリポジトリのtrunkから引っこ抜いておくれ。1.8限定でバイナリ置いてあるから。