| 
 
| 赞 | 0 |  
| VIP | 2 |  
| 好人卡 | 27 |  
| 积分 | 1 |  
| 经验 | 26327 |  
| 最后登录 | 2019-10-13 |  
| 在线时间 | 953 小时 |  
 Lv1.梦旅人 
	梦石0 星屑120 在线时间953 小时注册时间2007-4-25帖子805 | 
| 
本帖最后由 苏小脉 于 2012-12-31 05:40 编辑
x
加入我们,或者,欢迎回来。您需要 登录 才可以下载或查看,没有帐号?注册会员  
 Scorched End Audio Library
 
 原本是給 RGE2 做的音頻渲染庫,但由於 RGE2 目前基本沒有活躍開發,故而先獨立出來給各位 RM 用戶試用。這個庫可以給 3D 遊戲用,但也兼容 2D。
 
 Seal 是 Scorched End Audio Library 的簡稱,是從「焦尾音頻庫」翻譯過來的。相傳焦尾是東漢蔡邕所製名琴,與號鐘、繞梁、綠綺並稱為四大名琴。Seal 的底層是 OpenAL(相當於音頻界的 OpenGL),在其基礎上添加了對 Ogg Vorbis、MPEG1、2、3 和簡單 WAVE 格式的支持,同時也抽象了流式音頻的播放,在未來還會根據用戶的需求提供各種便捷的輔助方法、抽象層等。Seal 在原本的 OpenAL 的 C API 基礎上建立了 Ruby 綁定。
 
 Seal 與默認的音頻模塊
 
 默認的音頻模塊提供的是純粹的音頻重放,外加調節音量和音調的能力,除此之外並無其他特效。Seal 目前通過 OpenAL 支持對以下自然界的聲音現象的模擬:
 
 聲音的增長和衰減
 
 通俗的說就是聲源和觀察者之間的距離與觀察者聽到的聲音強度成正比。一個發聲物體離得越遠,觀察者附近的壓強震動就越小。比如牆上一把火炬,靠近它時燃燒的聲音變大,遠離它時聲音變小。RPG的場景中隨處都可以有這方面的應用——玩家走近市井,漸聞喧嘩;NPC 漸行漸遠,腳步聲漸悄。
 
 多普勒效應
 
 聲源和觀察者保持相對運動,當觀察者接收到聲波時,聲波的頻率與最初聲源發出的頻率和波長產生了差別的現象。比如當一輛救護車朝朝觀察者行駛而來,觀察者聽到的鳴笛聲會變得很急促,這是由於頻率變高、波長變短;當救護車駛過並遠離觀察者時,觀察者聽到的鳴笛聲則會變得很低沉,這是由於頻率變低、波長變長。遊戲中,法師的一個火球飛來,擦身而過的馬車都應該產生多普勒效應。
 
 聲音的空間定位
 
 人類通常可以識別出聲源在空間中的位置。前面提到的聲音的增長和衰減實際上是空間定位的一部分,因為通過聲音強度通常就能反過來推出發聲物體的距離,但僅限於距離,不含方向。玩家理應識別出兩個正在左手邊決鬥的NPC所發出的刀劍交擊聲是從左方傳來,而不是從右方。這個效果的具體表現手法是將音頻輸出到在不同位置的音頻輸出設備終端上。一對音箱或是當代筆記本電腦內置的揚聲器都有一左一右兩個輸出口,如此就把聲音的渲染從一個點提升到了一條線——聲音可以來自左邊,中間或者右邊。更多的輸出聲道和終端的搭配可以產生更真實的環繞音,把線提升到平面,平面提升到空間。
 
 迴響
 在特殊的環境中,一部分聲波被物體反射並逆向運動,與原有聲波交融所形成的效果(在特定場合下,反射回來的波甚至會再次被反射)。同樣的音效在不同的環境中會產生不同屬性的迴響——小屋內,管道中,森林中,空間站中,冰霜宮殿中……總之,有了對迴響效果的渲染,聲音的真實度便能大幅提升,尤其是在從一個環境切換到另一個環境中的時候那種聲音的驟變。
 
 其它
 除特效外,Seal 目前還具備 RM 所沒有的諸如播放時調節音量、音調,暫停播放、獲取音頻屬性等能力,當然仍然還有很多功能待實現,見「待完成」一欄。
 
 編程模型
 
 一個音頻數據文件的通過 Buffer 類或者 Stream 類對象加載——小巧的音頻數據用 Buffer 加載,大體積的音頻數據用 Stream 流式傳播。Buffer 或 Stream 的實例會被關聯上一個抽象地代表聲源的 Source 類對象,並通過 Source 的各種接口對音頻就行各種實時的操控、加工和過濾。全局可以有 n 個 Source 實例(遊戲中各種發聲的物體通常都是不同的 Source 實例),但只有一個 Listener 類的單一實例,表示觀察者(通常是場景中移動的玩家)。Source 的實例和觀察者都有位置和速度屬性,而實時更新這些屬性就能自動改變聲音在空間定位和減淡上的渲染。此間涉及到的大量數學計算都是底層引擎負責的,它最終會渲染出在某一時刻觀察者所在的地點聽到的把所有 Source 的聲音混合後的聲音。
 
 Seal 的模型和 RGSS 的圖形編程模型有很大的可比性,彼此的對象之間有大致的對應關係。
 
 Source 類似於 RGSS 的 Sprite,一個表示音頻,一個表示圖形,前者的不同實例最終集成於混音器的輸出,後者的不同實例最終集成到整個遊戲的圖形場景,兩者的存在都是為了方便對原有數據的實時加工渲染。
 
 Buffer 類似 Bitmap,前者是音頻數據的容器,後者是圖像數據的容器。通常用於加載小橋的音頻文件,比如 RM 的各種 BGS、ME 和 SE。
 
 Stream 從抽象層來看也基本等同於 Buffer,但從程序效率角度來看的話則大有不同——Buffer是將音頻一次性讀入內存,而 Stream 是流式數據傳輸。音頻數據通常比圖像數據大,一首5分鐘的MP3解壓之後會佔據50MB的空間,如果一次性讀入主內存那將會是很大的開銷。由於音頻播放的特性是只需要當前正在播放的這一部分數據,所以流式每次只讀取一小塊數據並傳輸給音頻輸出設備,而之前的數據佔據的內存則可以直接被釋放或重用。一個流隨時保持在內存中的數據只是原有音頻數據的很小一部分,就不會導致各種內存效率的問題(Cache miss,碎片,大量的頁缺失、內存顛簸)。通常需要用到流式的是背景音樂,比如 RM 的各種 BGM。
 
 迴響是 OpenAL 軟件實現的的特效擴展系列之一。在 Seal 中,迴響是通過控制另外兩種對象實現的:EffectSlot 和 Reverb(Reverb 是迴響的英文 Reverberation 的簡寫)。Reverb 的實例有各種關於迴響的參數設置,表示一種特定環境的迴響。Source 的實例可以選擇應用某種迴響,這時就需要使用 EffectSlot。EffectSlot 可以看作是流水線上作業的一個過濾器,負責過濾數字信號,將輸入的音頻應用上迴響的效果。EffectSlot 可以插入不同類型的效果,但由於目前 Seal 只實現了迴響的效果,所以目前僅限 Reverb。從設計模式角度來看,EffectSlot 就是適配器,統一了 Source 和特效對象之間的關聯接口。
 
 簡單流程
 
 初始化 Seal:
 
 Seal.startup include Seal # Seal 模塊是 Seal 的頂層命名空間,必要的時候可以 include 一下避免重複修飾 Seal::*
Seal.startup 
  
include Seal # Seal 模塊是 Seal 的頂層命名空間,必要的時候可以 include 一下避免重複修飾 Seal::* 
 利用 Source 對象表示聲源,並關聯上一個 Buffer 對象:
 
 source = Source.newsource.buffer = Buffer.new("audio.ogg")
source = Source.new 
source.buffer = Buffer.new("audio.ogg") 
 改變 source 的位置:
 
 source.position = 3, 2, -4  # 歐幾里德 3D 空間座標體系,2D 遊戲只要永遠保持第三個座標為 0 即可
source.position = 3, 2, -4  # 歐幾里德 3D 空間座標體系,2D 遊戲只要永遠保持第三個座標為 0 即可 
 改變 listener (觀察者,Seal::Listener 的單例)的位置:
 
 
 Seal.listener.position = -1, -1, 0
Seal.listener.position = -1, -1, 0 
 播放 source:
 
 
 需要播放大體積的音頻資源時則採用 Stream:
 
 source.stream = Stream.open("background_music.ogg")
source.stream = Stream.open("background_music.ogg") 
 從一個 buffer 切換到 stream 及其相反:
 
 
 source.buffer = ... # ... source.buffer = nilsource.stream = ... # ... source.stream = nilsource.buffer = ...
source.buffer = ... 
  
# ... 
  
source.buffer = nil 
source.stream = ... 
  
# ... 
  
source.stream = nil 
source.buffer = ... 
 應用迴響效果:
 
 # 分配一個 EffectSlot 對象,並關聯上一個 Reverb 對象slot = EffectSlot.new(Reverb.new(Reverb::Preset::FOREST))# 開始進行迴響效果的過濾source.feed(slot, 0)
# 分配一個 EffectSlot 對象,並關聯上一個 Reverb 對象 
slot = EffectSlot.new(Reverb.new(Reverb::Preset::FOREST)) 
# 開始進行迴響效果的過濾 
source.feed(slot, 0) 
 其中 Reverb::Preset 包含一系列預定義的迴響效果,如城堡、工廠、戶外、城市等。詳見:
 http://zhang.su/seal/Seal/Reverb/Preset.html
 
 最終化 Seal:
 
 
 詳細文檔
 在以下兩個地方都可以查閱:
 
 http://zhang.su/seal/
 http://rubydoc.info/gems/seal
 
 rubydoc.info 上那個可以看以前版本的。暫時還沒有精力寫中文文檔 TvT,先湊合看著英文吧……
 
 這裡有一些命令行的 demo 程序:
 https://github.com/zhangsu/seal/tree/master/demo
 
 注意事項
 
 
 只有單聲道的音頻數據文件在被 Seal 加載後才有空間定位、聲音減淡的效果。OpenAL 默認多聲道的音頻在設計的時候就考慮了立體音,所以不會強制改變它切換聲道的效果。多聲道音頻需要被事先轉換為單聲道音頻文件才能利用 Seal 的空間定位和減淡。Seal 並不支持解碼所有 WAVE 的子格式。Seal 只是出於測試的目的提供了對無壓縮的小於等於 16bps 的 WAVE 的支持,但實際上微軟官方的規格中有很多 WAVE 的子格式(包括壓縮失真格式)。建議大家統一使用 Ogg Vorbis 格式,開放自由。當 Source 被 GC 回收時,底層的對象被釋放,聲音的播放自然也會終止。管理好 Source 的作用域和生命週期,慎用局部變量。使用局部變量極容易出現聲音還沒有播放完就突然中斷的情況,原因是被 GC 回收了對象。用 VA 測試時發現有很小的幾率在調用 Seal.startup 時出現打開設備失敗的異常,但重試又好使了,原因不明,而且只是在 RM 環境中出現。目前的救急方案是:
 
 begin  Seal.startuprescue Seal::SealError  retryend
begin 
  Seal.startup 
rescue Seal::SealError 
  retry 
end 
 或者限制重試次數:
 
 
 3.times do  begin    Seal.startup    break  rescue Seal::SealError    next  endend
3.times do 
  begin 
    Seal.startup 
    break 
  rescue Seal::SealError 
    next 
  end 
end 
 
 下載
 
 首先必需的是 OpenAL 庫,這裡提供 MSVC 10.0 預編譯的 32 位二進制,你也可以自己編譯,只要是這裡的 OpenAL 軟件實現。
 
 
  OpenAL32.zip
(220.15 KB, 下载次数: 163) 
 RM 程序
 
 可以使用提供的 Win32API 綁定以及 seal 的 C 庫。庫文件(OpenAL32.dll,seal.dll)默認需要放在工程根目錄,如果需要更改,可以在 class SealAPI 下面添加一行:Win32API 綁定:
 
 module Seal  class SealAPI < Win32API    STRCPY_S = Win32API.new('msvcrt', 'strcpy_s', 'pll', 'i')     def initialize(func, arg_types, return_type = 'i', *args)      @return_string = return_type == 'p'      library = File.join(defined?(LIB_DIR) ? LIB_DIR : '.', 'seal')      super(library, "seal_#{func}", arg_types, return_type, *args)    end     def [](*args)      result = call(*args)      if @return_string and result.is_a? Integer        # String pointer is returned to Ruby as an integer even though we        # specified 'p' as the return value - possibly a bug in Ruby 1.9.3's        # Win32API implementation. Work around it.        message_buffer = ' ' * 128        STRCPY_S.call(message_buffer, message_buffer.size, result)        return message_buffer.strip      end      result    end unless method_defined? :[]  end   module Helper    GET_ERR_MSG = SealAPI.new('get_err_msg', 'i', 'p')     class << self      def define_enum(mod, constants, start_value = 0)        constants.each_with_index do |constant, index|          mod.const_set(constant, start_value + index)        end      end       # Returns a destructor for a native Seal object. This is most likely      # called in the initializer method, but we cannot define the proc handler      # there because that will capture the binding of the implicit `self` (due      # to the nature of closure), which makes the object that `self` refers to      # unrecyclable.      def free(obj, destroyer)        lambda { |object_id| destroyer[obj] }      end    end   private    def check_error(error_code)      raise SealError, GET_ERR_MSG[error_code], caller.shift if error_code != 0      nil    end     def input_audio(media, filename, format, inputter)      check_error(inputter[media, filename, format])    end     def set_obj_int(obj, int, setter)      check_error(setter[obj, int])      int    end     def get_obj_int(obj, getter)      buffer = '    '      check_error(getter[obj, buffer])      buffer.unpack('i')[0]    end     def set_obj_char(obj, bool, setter)      set_obj_int(obj, bool ? 1 : 0, setter)    end     def get_obj_char(obj, getter)      buffer = ' '      check_error(getter[obj, buffer])      buffer.unpack('c')[0] != 0    end     # Win32API does not support float argument type, need to pass as integer.    def set_obj_float(obj, float, setter)      check_error(setter[obj, [float].pack('f').unpack('i')[0]])      float    end     def get_obj_float(obj, getter)      float_buffer = '    '      check_error(getter[obj, float_buffer])      float_buffer.unpack('f')[0]    end  end   VERSION = SealAPI.new('get_version', 'v', 'p')[]   class << self    include Helper     STARTUP = SealAPI.new('startup', 'p')    CLEANUP = SealAPI.new('cleanup', 'v', 'v')    GET_PER_SRC_EFFECT_LIMIT = SealAPI.new('get_per_src_effect_limit', 'v')     def startup(device = nil)      check_error(STARTUP[device ? device : 0])    end     def cleanup      CLEANUP[]    end     def per_source_effect_limit      GET_PER_SRC_EFFECT_LIMIT[]    end  end   module Format    Helper.define_enum(self, [      :UNKNOWN,      :WAV,      :OV,      :MPG    ])  end   class SealError < Exception  end   class Listener    include Helper     SET_GAIN = SealAPI.new('set_listener_gain', 'i')    GET_GAIN = SealAPI.new('get_listener_gain', 'p')    SET_POS = SealAPI.new('set_listener_pos', 'iii')    GET_POS = SealAPI.new('get_listener_pos', 'ppp')    SET_VEL = SealAPI.new('set_listener_vel', 'iii')    GET_VEL = SealAPI.new('get_listener_vel', 'ppp')    SET_ORIEN = SealAPI.new('set_listener_orien', 'p')    GET_ORIEN = SealAPI.new('get_listener_orien', 'p')     def gain=(gain)      check_error(SET_GAIN[[gain].pack('f').unpack('i')[0]])      gain    end     def gain      gain_buffer = '    '      check_error(GET_GAIN[gain_buffer])      return gain_buffer.unpack('f')[0]    end     def position=(position)      set_3float(position, SET_POS)    end     def position      get_3float(GET_POS)    end     def velocity=(velocity)      set_3float(velocity, SET_VEL)    end     def velocity      get_3float(GET_VEL)    end     def orientation=(orientation)      check_error(SET_ORIEN[orientation.flatten.pack('f*')])    end     def orientation      orientation_buffer = '    ' * 6      check_error(GET_ORIEN[orientation_buffer])      orientation = orientation_buffer.unpack('f*')      [orientation[0..2], orientation[3..5]]    end   private    def set_3float(float_tuple, setter)      integer_tuple = float_tuple.pack('f*').unpack('i*')      check_error(setter[*integer_tuple])      return float_tuple    end     def get_3float(getter)      float_tuple_buffers = Array.new(3) { '    ' }      check_error(getter[*float_tuple_buffers])      return float_tuple_buffers.join.unpack('f*')    end  end   LISTENER = Listener.new   def self.listener    LISTENER  end   class << Listener    undef allocate    undef new  end   class Buffer    include Helper     INIT = SealAPI.new('init_buf', 'p')    DESTROY = SealAPI.new('destroy_buf', 'p')    LOAD = SealAPI.new('load2buf', 'ppi')    GET_SIZE = SealAPI.new('get_buf_size', 'pp')    GET_FREQ = SealAPI.new('get_buf_freq', 'pp')    GET_BPS = SealAPI.new('get_buf_bps', 'pp')    GET_NCHANNELS = SealAPI.new('get_buf_nchannels', 'pp')     def initialize(filename, format = Format::UNKNOWN)      @buffer = '    '      check_error(INIT[@buffer])      input_audio(@buffer, filename, format, LOAD)      ObjectSpace.define_finalizer(self, Helper.free(@buffer, DESTROY))      self    end     def load(filename, format = Format::UNKNOWN)      input_audio(@buffer, filename, format, LOAD)      self    end     def size      get_obj_int(@buffer, GET_SIZE)    end     def frequency      get_obj_int(@buffer, GET_FREQ)    end     def bit_depth      get_obj_int(@buffer, GET_BPS)    end     def channel_count      get_obj_int(@buffer, GET_NCHANNELS)    end  end   class Stream    include Helper     OPEN = SealAPI.new('open_stream', 'ppi')    CLOSE = SealAPI.new('close_stream', 'p')    REWIND = SealAPI.new('rewind_stream', 'p')     class << self      alias open new    end     def initialize(filename, format = Format::UNKNOWN)      [url=home.php?mod=space&uid=33438]@stream[/url] = '    ' * 5      input_audio(@stream, filename, format, OPEN)      ObjectSpace.define_finalizer(self, Helper.free(@stream, CLOSE))      self    end     def frequency      field(4)    end     def bit_depth      field(2)    end     def channel_count      field(3)    end     def rewind      check_error(REWIND[@stream])    end     def close      check_error(CLOSE[@stream])    end   private    def field(index)      @stream[index * 4, 4].unpack('i')[0]    end  end   class Reverb    include Helper     INIT = SealAPI.new('init_rvb', 'p')    DESTROY = SealAPI.new('destroy_rvb', 'p')    LOAD = SealAPI.new('load_rvb', 'pi')    SET_DENSITY = SealAPI.new('set_rvb_density', 'pi')    SET_DIFFUSION = SealAPI.new('set_rvb_diffusion', 'pi')    SET_GAIN = SealAPI.new('set_rvb_gain', 'pi')    SET_HFGAIN = SealAPI.new('set_rvb_hfgain', 'pi')    SET_DECAY_TIME = SealAPI.new('set_rvb_decay_time', 'pi')    SET_HFDECAY_RATIO = SealAPI.new('set_rvb_hfdecay_ratio', 'pi')    SET_REFLECTIONS_GAIN = SealAPI.new('set_rvb_reflections_gain', 'pi')    SET_REFLECTIONS_DELAY = SealAPI.new('set_rvb_reflections_delay', 'pi')    SET_LATE_GAIN = SealAPI.new('set_rvb_late_gain', 'pi')    SET_LATE_DELAY = SealAPI.new('set_rvb_late_delay', 'pi')    SET_AIR_ABSORBTION_HFGAIN =      SealAPI.new('set_rvb_air_absorbtion_hfgain', 'pi')    SET_ROOM_ROLLOFF_FACTOR = SealAPI.new('set_rvb_room_rolloff_factor', 'pi')    SET_HFDECAY_LIMITED = SealAPI.new('set_rvb_hfdecay_limited', 'pi')    GET_DENSITY = SealAPI.new('get_rvb_density', 'pp')    GET_DIFFUSION = SealAPI.new('get_rvb_diffusion', 'pp')    GET_GAIN = SealAPI.new('get_rvb_gain', 'pp')    GET_HFGAIN = SealAPI.new('get_rvb_hfgain', 'pp')    GET_DECAY_TIME = SealAPI.new('get_rvb_decay_time', 'pp')    GET_HFDECAY_RATIO = SealAPI.new('get_rvb_hfdecay_ratio', 'pp')    GET_REFLECTIONS_GAIN = SealAPI.new('get_rvb_reflections_gain', 'pp')    GET_REFLECTIONS_DELAY = SealAPI.new('get_rvb_reflections_delay', 'pp')    GET_LATE_GAIN = SealAPI.new('get_rvb_late_gain', 'pp')    GET_LATE_DELAY = SealAPI.new('get_rvb_late_delay', 'pp')    GET_AIR_ABSORBTION_HFGAIN =      SealAPI.new('get_rvb_air_absorbtion_hfgain', 'pp')    GET_ROOM_ROLLOFF_FACTOR = SealAPI.new('get_rvb_room_rolloff_factor', 'pp')    IS_HFDECAY_LIMITED = SealAPI.new('is_rvb_hfdecay_limited', 'pp')     def initialize(preset = nil)      @effect = '    '      check_error(INIT[@effect])      load(preset) if preset      ObjectSpace.define_finalizer(self, Helper.free(@effect, DESTROY))      self    end     def load(preset)      check_error(LOAD[@effect, preset])    end     def density=(density)      set_obj_float(@effect, density, SET_DENSITY)    end     def diffusion=(diffusion)      set_obj_float(@effect, diffusion, SET_DIFFUSION)    end     def gain=(gain)      set_obj_float(@effect, gain, SET_GAIN)    end     def hfgain=(hfgain)      set_obj_float(@effect, hfgain, SET_HFGAIN)    end     def decay_time=(decay_time)      set_obj_float(@effect, decay_time, SET_DECAY_TIME)    end     def hfdecay_ratio=(hfdecay_ratio)      set_obj_float(@effect, hfdecay_ratio, SET_HFDECAY_RATIO)    end     def reflections_gain=(reflections_gain)      set_obj_float(@effect, reflections_gain, SET_REFLECTIONS_GAIN)    end     def reflections_delay=(reflections_delay)      set_obj_float(@effect, reflections_delay, SET_REFLECTIONS_DELAY)    end     def late_gain=(late_gain)      set_obj_float(@effect, late_gain, SET_LATE_GAIN)    end     def late_delay=(late_delay)      set_obj_float(@effect, late_delay, SET_LATE_DELAY)    end     def air_absorbtion_hfgain=(air_absorbtion_hfgain)      set_obj_float(@effect, air_absorbtion_hfgain, SET_AIR_ABSORBTION_HFGAIN)    end     def room_rolloff_factor=(room_rolloff_factor)      set_obj_float(@effect, room_rolloff_factor, SET_ROOM_ROLLOFF_FACTOR)    end     def hfdecay_limited=(hfdecay_limited)      set_obj_char(@effect, hfdecay_limited, SET_HFDECAY_LIMITED)    end     def density      get_obj_float(@effect, GET_DENSITY)    end     def diffusion      get_obj_float(@effect, GET_DIFFUSION)    end     def gain      get_obj_float(@effect, GET_GAIN)    end     def hfgain      get_obj_float(@effect, GET_HFGAIN)    end     def decay_time      get_obj_float(@effect, GET_DECAY_TIME)    end     def hfdecay_ratio      get_obj_float(@effect, GET_HFDECAY_RATIO)    end     def reflections_gain      get_obj_float(@effect, GET_REFLECTIONS_GAIN)    end     def reflections_delay      get_obj_float(@effect, GET_REFLECTIONS_DELAY)    end     def late_gain      get_obj_float(@effect, GET_LATE_GAIN)    end     def late_delay      get_obj_float(@effect, GET_LATE_DELAY)    end     def air_absorbtion_hfgain      get_obj_float(@effect, GET_AIR_ABSORBTION_HFGAIN)    end     def room_rolloff_factor      get_obj_float(@effect, GET_ROOM_ROLLOFF_FACTOR)    end     def hfdecay_limited      get_obj_char(@effect, IS_HFDECAY_LIMITED)    end     alias hfdecay_limited? hfdecay_limited     module Preset      Helper.define_enum(self, [        :GENERIC,        :PADDEDCELL,        :ROOM,        :BATHROOM,        :LIVINGROOM,        :STONEROOM,        :AUDITORIUM,        :CONCERTHALL,        :CAVE,        :ARENA,        :HANGAR,        :CARPETEDHALLWAY,        :HALLWAY,        :STONECORRIDOR,        :ALLEY,        :FOREST,        :CITY,        :MOUNTAINS,        :QUARRY,        :PLAIN,        :PARKINGLOT,        :SEWERPIPE,        :UNDERWATER,        :DRUGGED,        :DIZZY,        :PSYCHOTIC      ])       module Castle        Helper.define_enum(self, [          :SMALLROOM,          :SHORTPASSAGE,          :MEDIUMROOM,          :LARGEROOM,          :LONGPASSAGE,          :HALL,          :CUPBOARD,          :COURTYARD,          :ALCOVE        ], Preset::PSYCHOTIC + 1)      end       module Factory        Helper.define_enum(self, [          :SMALLROOM,          :SHORTPASSAGE,          :MEDIUMROOM,          :LARGEROOM,          :LONGPASSAGE,          :HALL,          :CUPBOARD,          :COURTYARD,          :ALCOVE        ], Castle::ALCOVE + 1)      end       module IcePalace        Helper.define_enum(self, [          :SMALLROOM,          :SHORTPASSAGE,          :MEDIUMROOM,          :LARGEROOM,          :LONGPASSAGE,          :HALL,          :CUPBOARD,          :COURTYARD,          :ALCOVE        ], Factory::ALCOVE + 1)      end       module SpaceStation        Helper.define_enum(self, [          :SMALLROOM,          :SHORTPASSAGE,          :MEDIUMROOM,          :LARGEROOM,          :LONGPASSAGE,          :HALL,          :CUPBOARD,          :ALCOVE        ], IcePalace::ALCOVE + 1)      end       module WoodenGalleon        Helper.define_enum(self, [          :SMALLROOM,          :SHORTPASSAGE,          :MEDIUMROOM,          :LARGEROOM,          :LONGPASSAGE,          :HALL,          :CUPBOARD,          :COURTYARD,          :ALCOVE        ], SpaceStation::ALCOVE + 1)      end       module Sports        Helper.define_enum(self, [          :EMPTYSTADIUM,          :SQUASHCOURT,          :SMALLSWIMMINGPOOL,          :LARGESWIMMINGPOOL,          :GYMNASIUM,          :FULLSTADIUM,          :STADIUMTANNOY        ], WoodenGalleon::ALCOVE + 1)      end       module Prefab        Helper.define_enum(self, [          :WORKSHOP,          :SCHOOLROOM,          :PRACTISEROOM,          :OUTHOUSE,          :CARAVAN        ], Sports::STADIUMTANNOY + 1)      end       module Dome        Helper.define_enum(self, [          :TOMB,          :SAINTPAULS        ], Prefab::CARAVAN + 1)      end       module Pipe        Helper.define_enum(self, [          :SMALL,          :LONGTHIN,          :LARGE,          :RESONANT        ], Dome::SAINTPAULS + 1)      end       module Outdoors        Helper.define_enum(self, [          :BACKYARD,          :ROLLINGPLAINS,          :DEEPCANYON,          :CREEK,          :VALLEY        ], Pipe::RESONANT + 1)      end       module Mood        Helper.define_enum(self, [          :HEAVEN,          :HELL,          :MEMORY        ], Outdoors::VALLEY + 1)      end       module Driving        Helper.define_enum(self, [          :COMMENTATOR,          :PITGARAGE,          :INCAR_RACER,          :INCAR_SPORTS,          :INCAR_LUXURY,          :FULLGRANDSTAND,          :EMPTYGRANDSTAND,          :TUNNEL        ], Mood::MEMORY + 1)      end       module City        Helper.define_enum(self, [          :STREETS,          :SUBWAY,          :MUSEUM,          :LIBRARY,          :UNDERPASS,          :ABANDONED        ], Driving::TUNNEL + 1)      end       module Misc        Helper.define_enum(self, [          :DUSTYROOM,          :CHAPEL,          :SMALLWATERROOM        ], City::ABANDONED + 1)      end    end  end   class Source    include Helper     INIT = SealAPI.new('init_src', 'p')    DESTROY = SealAPI.new('destroy_src', 'p')    PLAY = SealAPI.new('play_src', 'p')    STOP = SealAPI.new('stop_src', 'p')    REWIND = SealAPI.new('rewind_src', 'p')    PAUSE = SealAPI.new('pause_src', 'p')    DETACH = SealAPI.new('detach_src_audio', 'p')    SET_BUF = SealAPI.new('set_src_buf', 'pp')    SET_STREAM = SealAPI.new('set_src_stream', 'pp')    FEED_EFS = SealAPI.new('feed_efs', 'ppi')    UPDATE = SealAPI.new('update_src', 'p')    SET_POS = SealAPI.new('set_src_pos', 'piii')    SET_VEL = SealAPI.new('set_src_vel', 'piii')    SET_GAIN = SealAPI.new('set_src_gain', 'pi')    SET_PITCH = SealAPI.new('set_src_pitch', 'pi')    SET_AUTO = SealAPI.new('set_src_auto', 'pi')    SET_RELATIVE = SealAPI.new('set_src_relative', 'pi')    SET_LOOPING = SealAPI.new('set_src_looping', 'pi')    SET_QUEUE_SIZE = SealAPI.new('set_src_queue_size', 'pi')    SET_CHUNK_SIZE = SealAPI.new('set_src_chunk_size', 'pi')    GET_POS = SealAPI.new('get_src_pos', 'pppp')    GET_VEL = SealAPI.new('get_src_vel', 'pppp')    GET_GAIN = SealAPI.new('get_src_gain', 'pp')    GET_PITCH = SealAPI.new('get_src_pitch', 'pp')    GET_AUTO = SealAPI.new('is_src_auto', 'pp')    GET_RELATIVE = SealAPI.new('is_src_relative', 'pp')    GET_LOOPING = SealAPI.new('is_src_looping', 'pp')    GET_QUEUE_SIZE = SealAPI.new('get_src_queue_size', 'pp')    GET_CHUNK_SIZE = SealAPI.new('get_src_chunk_size', 'pp')    GET_TYPE = SealAPI.new('get_src_type', 'pp')    GET_STATE = SealAPI.new('get_src_state', 'pp')     def initialize      [url=home.php?mod=space&uid=171370]@source[/url] = '    ' * 5      check_error(INIT[@source])      ObjectSpace.define_finalizer(self, Helper.free(@source, DESTROY))      self    end     def play      operate(PLAY)    end     def stop      operate(STOP)    end     def rewind      operate(REWIND)    end     def pause      operate(PAUSE)    end     def buffer=(buffer)      set_audio(:@buffer, buffer, SET_BUF)    end     def stream=(stream)      set_audio(:@stream, stream, SET_STREAM)    end     attr_reader :buffer, :stream     def feed(effect_slot, index)      native_efs_obj = effect_slot.instance_variable_get(:@effect_slot)      check_error(FEED_EFS[@source, native_efs_obj, index])      self    end     def update      operate(UPDATE)    end     def position=(position)      set_3float(position, SET_POS)    end     def velocity=(velocity)      set_3float(velocity, SET_VEL)    end     def gain=(gain)      set_obj_float(@source, gain, SET_GAIN)    end     def pitch=(pitch)      set_obj_float(@source, pitch, SET_PITCH)    end     def auto=(auto)      set_obj_char(@source, auto, SET_AUTO)    end     def queue_size=(queue_size)      set_obj_int(@source, queue_size, SET_QUEUE_SIZE)    end     def chunk_size=(chunk_size)      set_obj_int(@source, chunk_size, SET_CHUNK_SIZE)    end     def relative=(relative)      set_obj_char(@source, relative, SET_RELATIVE)    end     def looping=(looping)      set_obj_char(@source, looping, SET_LOOPING)    end     def position      get_3float(GET_POS)    end     def velocity      get_3float(GET_VEL)    end     def gain      get_obj_float(@source, GET_GAIN)    end     def pitch      get_obj_float(@source, GET_PITCH)    end     def auto      get_obj_char(@source, GET_AUTO)    end     def relative      get_obj_char(@source, GET_RELATIVE)    end     def looping      get_obj_char(@source, GET_LOOPING)    end     alias auto? auto    alias relative? relative    alias looping? looping     def queue_size      get_obj_int(@source, GET_QUEUE_SIZE)    end     def chunk_size      get_obj_int(@source, GET_CHUNK_SIZE)    end     def type      case get_obj_int(@source, GET_TYPE)      when Type::STATIC        Type::STATIC      when Type::STREAMING        Type::STREAMING      else        Type::UNDETERMINED      end    end     def state      case get_obj_int(@source, GET_STATE)      when State::PLAYING        State::PLAYING      when State::PAUSED        State::PAUSED      when State::STOPPED        State::STOPPED      else        State::INITIAL      end    end   private    def operate(operation)      check_error(operation[@source])      self    end     def set_audio(var, audio, setter)      if audio.nil?        operate(DETACH)      else        check_error(setter[@source, audio.instance_variable_get(var)])      end      instance_variable_set(var, audio)      audio    end     def set_3float(float_tuple, setter)      integer_tuple = float_tuple.pack('f*').unpack('i*')      check_error(setter[@source, *integer_tuple])      float_tuple    end     def get_3float(getter)      float_tuple_buffers = Array.new(3) { '    ' }      check_error(getter[@source, *float_tuple_buffers])      float_tuple_buffers.join.unpack('f*')    end     module State      Helper.define_enum(self, [        :INITIAL,        :PLAYING,        :PAUSED,        :STOPPED      ])    end     module Type      Helper.define_enum(self, [        :UNDETERMINED,        :STATIC,        :STREAMING      ])    end  end   class EffectSlot    include Helper     INIT = SealAPI.new('init_efs', 'p')    DESTROY = SealAPI.new('destroy_efs', 'p')    SET_EFFECT = SealAPI.new('set_efs_effect', 'pp')    SET_GAIN = SealAPI.new('set_efs_gain', 'pi')    SET_AUTO = SealAPI.new('set_efs_auto', 'pi')    GET_GAIN = SealAPI.new('get_efs_gain', 'pp')    GET_AUTO = SealAPI.new('is_efs_auto', 'pp')     def initialize(effect = nil)      @effect_slot = '    '      check_error(INIT[@effect_slot])      self.effect = effect if effect      ObjectSpace.define_finalizer(self, Helper.free(@effect_slot, DESTROY))      self    end     def effect=(effect)      native_effect_obj = effect ? effect.instance_variable_get(:@effect) : 0      check_error(SET_EFFECT[@effect_slot, native_effect_obj])      @effect = effect      effect    end     attr_reader :effect     def gain=(gain)      set_obj_float(@effect_slot, gain, SET_GAIN)    end     def gain      get_obj_float(@effect_slot, GET_GAIN)    end     def auto=(auto)      set_obj_char(@effect_slot, auto, SET_AUTO)    end     def auto      get_obj_char(@effect_slot, GET_AUTO)    end     alias auto? auto  endend
module Seal 
  class SealAPI < Win32API 
    STRCPY_S = Win32API.new('msvcrt', 'strcpy_s', 'pll', 'i') 
  
    def initialize(func, arg_types, return_type = 'i', *args) 
      @return_string = return_type == 'p' 
      library = File.join(defined?(LIB_DIR) ? LIB_DIR : '.', 'seal') 
      super(library, "seal_#{func}", arg_types, return_type, *args) 
    end 
  
    def [](*args) 
      result = call(*args) 
      if @return_string and result.is_a? Integer 
        # String pointer is returned to Ruby as an integer even though we 
        # specified 'p' as the return value - possibly a bug in Ruby 1.9.3's 
        # Win32API implementation. Work around it. 
        message_buffer = ' ' * 128 
        STRCPY_S.call(message_buffer, message_buffer.size, result) 
        return message_buffer.strip 
      end 
      result 
    end unless method_defined? :[] 
  end 
  
  module Helper 
    GET_ERR_MSG = SealAPI.new('get_err_msg', 'i', 'p') 
  
    class << self 
      def define_enum(mod, constants, start_value = 0) 
        constants.each_with_index do |constant, index| 
          mod.const_set(constant, start_value + index) 
        end 
      end 
  
      # Returns a destructor for a native Seal object. This is most likely 
      # called in the initializer method, but we cannot define the proc handler 
      # there because that will capture the binding of the implicit `self` (due 
      # to the nature of closure), which makes the object that `self` refers to 
      # unrecyclable. 
      def free(obj, destroyer) 
        lambda { |object_id| destroyer[obj] } 
      end 
    end 
  
  private 
    def check_error(error_code) 
      raise SealError, GET_ERR_MSG[error_code], caller.shift if error_code != 0 
      nil 
    end 
  
    def input_audio(media, filename, format, inputter) 
      check_error(inputter[media, filename, format]) 
    end 
  
    def set_obj_int(obj, int, setter) 
      check_error(setter[obj, int]) 
      int 
    end 
  
    def get_obj_int(obj, getter) 
      buffer = '    ' 
      check_error(getter[obj, buffer]) 
      buffer.unpack('i')[0] 
    end 
  
    def set_obj_char(obj, bool, setter) 
      set_obj_int(obj, bool ? 1 : 0, setter) 
    end 
  
    def get_obj_char(obj, getter) 
      buffer = ' ' 
      check_error(getter[obj, buffer]) 
      buffer.unpack('c')[0] != 0 
    end 
  
    # Win32API does not support float argument type, need to pass as integer. 
    def set_obj_float(obj, float, setter) 
      check_error(setter[obj, [float].pack('f').unpack('i')[0]]) 
      float 
    end 
  
    def get_obj_float(obj, getter) 
      float_buffer = '    ' 
      check_error(getter[obj, float_buffer]) 
      float_buffer.unpack('f')[0] 
    end 
  end 
  
  VERSION = SealAPI.new('get_version', 'v', 'p')[] 
  
  class << self 
    include Helper 
  
    STARTUP = SealAPI.new('startup', 'p') 
    CLEANUP = SealAPI.new('cleanup', 'v', 'v') 
    GET_PER_SRC_EFFECT_LIMIT = SealAPI.new('get_per_src_effect_limit', 'v') 
  
    def startup(device = nil) 
      check_error(STARTUP[device ? device : 0]) 
    end 
  
    def cleanup 
      CLEANUP[] 
    end 
  
    def per_source_effect_limit 
      GET_PER_SRC_EFFECT_LIMIT[] 
    end 
  end 
  
  module Format 
    Helper.define_enum(self, [ 
      :UNKNOWN, 
      :WAV, 
      :OV, 
      :MPG 
    ]) 
  end 
  
  class SealError < Exception 
  end 
  
  class Listener 
    include Helper 
  
    SET_GAIN = SealAPI.new('set_listener_gain', 'i') 
    GET_GAIN = SealAPI.new('get_listener_gain', 'p') 
    SET_POS = SealAPI.new('set_listener_pos', 'iii') 
    GET_POS = SealAPI.new('get_listener_pos', 'ppp') 
    SET_VEL = SealAPI.new('set_listener_vel', 'iii') 
    GET_VEL = SealAPI.new('get_listener_vel', 'ppp') 
    SET_ORIEN = SealAPI.new('set_listener_orien', 'p') 
    GET_ORIEN = SealAPI.new('get_listener_orien', 'p') 
  
    def gain=(gain) 
      check_error(SET_GAIN[[gain].pack('f').unpack('i')[0]]) 
      gain 
    end 
  
    def gain 
      gain_buffer = '    ' 
      check_error(GET_GAIN[gain_buffer]) 
      return gain_buffer.unpack('f')[0] 
    end 
  
    def position=(position) 
      set_3float(position, SET_POS) 
    end 
  
    def position 
      get_3float(GET_POS) 
    end 
  
    def velocity=(velocity) 
      set_3float(velocity, SET_VEL) 
    end 
  
    def velocity 
      get_3float(GET_VEL) 
    end 
  
    def orientation=(orientation) 
      check_error(SET_ORIEN[orientation.flatten.pack('f*')]) 
    end 
  
    def orientation 
      orientation_buffer = '    ' * 6 
      check_error(GET_ORIEN[orientation_buffer]) 
      orientation = orientation_buffer.unpack('f*') 
      [orientation[0..2], orientation[3..5]] 
    end 
  
  private 
    def set_3float(float_tuple, setter) 
      integer_tuple = float_tuple.pack('f*').unpack('i*') 
      check_error(setter[*integer_tuple]) 
      return float_tuple 
    end 
  
    def get_3float(getter) 
      float_tuple_buffers = Array.new(3) { '    ' } 
      check_error(getter[*float_tuple_buffers]) 
      return float_tuple_buffers.join.unpack('f*') 
    end 
  end 
  
  LISTENER = Listener.new 
  
  def self.listener 
    LISTENER 
  end 
  
  class << Listener 
    undef allocate 
    undef new 
  end 
  
  class Buffer 
    include Helper 
  
    INIT = SealAPI.new('init_buf', 'p') 
    DESTROY = SealAPI.new('destroy_buf', 'p') 
    LOAD = SealAPI.new('load2buf', 'ppi') 
    GET_SIZE = SealAPI.new('get_buf_size', 'pp') 
    GET_FREQ = SealAPI.new('get_buf_freq', 'pp') 
    GET_BPS = SealAPI.new('get_buf_bps', 'pp') 
    GET_NCHANNELS = SealAPI.new('get_buf_nchannels', 'pp') 
  
    def initialize(filename, format = Format::UNKNOWN) 
      @buffer = '    ' 
      check_error(INIT[@buffer]) 
      input_audio(@buffer, filename, format, LOAD) 
      ObjectSpace.define_finalizer(self, Helper.free(@buffer, DESTROY)) 
      self 
    end 
  
    def load(filename, format = Format::UNKNOWN) 
      input_audio(@buffer, filename, format, LOAD) 
      self 
    end 
  
    def size 
      get_obj_int(@buffer, GET_SIZE) 
    end 
  
    def frequency 
      get_obj_int(@buffer, GET_FREQ) 
    end 
  
    def bit_depth 
      get_obj_int(@buffer, GET_BPS) 
    end 
  
    def channel_count 
      get_obj_int(@buffer, GET_NCHANNELS) 
    end 
  end 
  
  class Stream 
    include Helper 
  
    OPEN = SealAPI.new('open_stream', 'ppi') 
    CLOSE = SealAPI.new('close_stream', 'p') 
    REWIND = SealAPI.new('rewind_stream', 'p') 
  
    class << self 
      alias open new 
    end 
  
    def initialize(filename, format = Format::UNKNOWN) 
      [url=home.php?mod=space&uid=33438]@stream[/url] = '    ' * 5 
      input_audio(@stream, filename, format, OPEN) 
      ObjectSpace.define_finalizer(self, Helper.free(@stream, CLOSE)) 
      self 
    end 
  
    def frequency 
      field(4) 
    end 
  
    def bit_depth 
      field(2) 
    end 
  
    def channel_count 
      field(3) 
    end 
  
    def rewind 
      check_error(REWIND[@stream]) 
    end 
  
    def close 
      check_error(CLOSE[@stream]) 
    end 
  
  private 
    def field(index) 
      @stream[index * 4, 4].unpack('i')[0] 
    end 
  end 
  
  class Reverb 
    include Helper 
  
    INIT = SealAPI.new('init_rvb', 'p') 
    DESTROY = SealAPI.new('destroy_rvb', 'p') 
    LOAD = SealAPI.new('load_rvb', 'pi') 
    SET_DENSITY = SealAPI.new('set_rvb_density', 'pi') 
    SET_DIFFUSION = SealAPI.new('set_rvb_diffusion', 'pi') 
    SET_GAIN = SealAPI.new('set_rvb_gain', 'pi') 
    SET_HFGAIN = SealAPI.new('set_rvb_hfgain', 'pi') 
    SET_DECAY_TIME = SealAPI.new('set_rvb_decay_time', 'pi') 
    SET_HFDECAY_RATIO = SealAPI.new('set_rvb_hfdecay_ratio', 'pi') 
    SET_REFLECTIONS_GAIN = SealAPI.new('set_rvb_reflections_gain', 'pi') 
    SET_REFLECTIONS_DELAY = SealAPI.new('set_rvb_reflections_delay', 'pi') 
    SET_LATE_GAIN = SealAPI.new('set_rvb_late_gain', 'pi') 
    SET_LATE_DELAY = SealAPI.new('set_rvb_late_delay', 'pi') 
    SET_AIR_ABSORBTION_HFGAIN = 
      SealAPI.new('set_rvb_air_absorbtion_hfgain', 'pi') 
    SET_ROOM_ROLLOFF_FACTOR = SealAPI.new('set_rvb_room_rolloff_factor', 'pi') 
    SET_HFDECAY_LIMITED = SealAPI.new('set_rvb_hfdecay_limited', 'pi') 
    GET_DENSITY = SealAPI.new('get_rvb_density', 'pp') 
    GET_DIFFUSION = SealAPI.new('get_rvb_diffusion', 'pp') 
    GET_GAIN = SealAPI.new('get_rvb_gain', 'pp') 
    GET_HFGAIN = SealAPI.new('get_rvb_hfgain', 'pp') 
    GET_DECAY_TIME = SealAPI.new('get_rvb_decay_time', 'pp') 
    GET_HFDECAY_RATIO = SealAPI.new('get_rvb_hfdecay_ratio', 'pp') 
    GET_REFLECTIONS_GAIN = SealAPI.new('get_rvb_reflections_gain', 'pp') 
    GET_REFLECTIONS_DELAY = SealAPI.new('get_rvb_reflections_delay', 'pp') 
    GET_LATE_GAIN = SealAPI.new('get_rvb_late_gain', 'pp') 
    GET_LATE_DELAY = SealAPI.new('get_rvb_late_delay', 'pp') 
    GET_AIR_ABSORBTION_HFGAIN = 
      SealAPI.new('get_rvb_air_absorbtion_hfgain', 'pp') 
    GET_ROOM_ROLLOFF_FACTOR = SealAPI.new('get_rvb_room_rolloff_factor', 'pp') 
    IS_HFDECAY_LIMITED = SealAPI.new('is_rvb_hfdecay_limited', 'pp') 
  
    def initialize(preset = nil) 
      @effect = '    ' 
      check_error(INIT[@effect]) 
      load(preset) if preset 
      ObjectSpace.define_finalizer(self, Helper.free(@effect, DESTROY)) 
      self 
    end 
  
    def load(preset) 
      check_error(LOAD[@effect, preset]) 
    end 
  
    def density=(density) 
      set_obj_float(@effect, density, SET_DENSITY) 
    end 
  
    def diffusion=(diffusion) 
      set_obj_float(@effect, diffusion, SET_DIFFUSION) 
    end 
  
    def gain=(gain) 
      set_obj_float(@effect, gain, SET_GAIN) 
    end 
  
    def hfgain=(hfgain) 
      set_obj_float(@effect, hfgain, SET_HFGAIN) 
    end 
  
    def decay_time=(decay_time) 
      set_obj_float(@effect, decay_time, SET_DECAY_TIME) 
    end 
  
    def hfdecay_ratio=(hfdecay_ratio) 
      set_obj_float(@effect, hfdecay_ratio, SET_HFDECAY_RATIO) 
    end 
  
    def reflections_gain=(reflections_gain) 
      set_obj_float(@effect, reflections_gain, SET_REFLECTIONS_GAIN) 
    end 
  
    def reflections_delay=(reflections_delay) 
      set_obj_float(@effect, reflections_delay, SET_REFLECTIONS_DELAY) 
    end 
  
    def late_gain=(late_gain) 
      set_obj_float(@effect, late_gain, SET_LATE_GAIN) 
    end 
  
    def late_delay=(late_delay) 
      set_obj_float(@effect, late_delay, SET_LATE_DELAY) 
    end 
  
    def air_absorbtion_hfgain=(air_absorbtion_hfgain) 
      set_obj_float(@effect, air_absorbtion_hfgain, SET_AIR_ABSORBTION_HFGAIN) 
    end 
  
    def room_rolloff_factor=(room_rolloff_factor) 
      set_obj_float(@effect, room_rolloff_factor, SET_ROOM_ROLLOFF_FACTOR) 
    end 
  
    def hfdecay_limited=(hfdecay_limited) 
      set_obj_char(@effect, hfdecay_limited, SET_HFDECAY_LIMITED) 
    end 
  
    def density 
      get_obj_float(@effect, GET_DENSITY) 
    end 
  
    def diffusion 
      get_obj_float(@effect, GET_DIFFUSION) 
    end 
  
    def gain 
      get_obj_float(@effect, GET_GAIN) 
    end 
  
    def hfgain 
      get_obj_float(@effect, GET_HFGAIN) 
    end 
  
    def decay_time 
      get_obj_float(@effect, GET_DECAY_TIME) 
    end 
  
    def hfdecay_ratio 
      get_obj_float(@effect, GET_HFDECAY_RATIO) 
    end 
  
    def reflections_gain 
      get_obj_float(@effect, GET_REFLECTIONS_GAIN) 
    end 
  
    def reflections_delay 
      get_obj_float(@effect, GET_REFLECTIONS_DELAY) 
    end 
  
    def late_gain 
      get_obj_float(@effect, GET_LATE_GAIN) 
    end 
  
    def late_delay 
      get_obj_float(@effect, GET_LATE_DELAY) 
    end 
  
    def air_absorbtion_hfgain 
      get_obj_float(@effect, GET_AIR_ABSORBTION_HFGAIN) 
    end 
  
    def room_rolloff_factor 
      get_obj_float(@effect, GET_ROOM_ROLLOFF_FACTOR) 
    end 
  
    def hfdecay_limited 
      get_obj_char(@effect, IS_HFDECAY_LIMITED) 
    end 
  
    alias hfdecay_limited? hfdecay_limited 
  
    module Preset 
      Helper.define_enum(self, [ 
        :GENERIC, 
        :PADDEDCELL, 
        :ROOM, 
        :BATHROOM, 
        :LIVINGROOM, 
        :STONEROOM, 
        :AUDITORIUM, 
        :CONCERTHALL, 
        :CAVE, 
        :ARENA, 
        :HANGAR, 
        :CARPETEDHALLWAY, 
        :HALLWAY, 
        :STONECORRIDOR, 
        :ALLEY, 
        :FOREST, 
        :CITY, 
        :MOUNTAINS, 
        :QUARRY, 
        :PLAIN, 
        :PARKINGLOT, 
        :SEWERPIPE, 
        :UNDERWATER, 
        :DRUGGED, 
        :DIZZY, 
        :PSYCHOTIC 
      ]) 
  
      module Castle 
        Helper.define_enum(self, [ 
          :SMALLROOM, 
          :SHORTPASSAGE, 
          :MEDIUMROOM, 
          :LARGEROOM, 
          :LONGPASSAGE, 
          :HALL, 
          :CUPBOARD, 
          :COURTYARD, 
          :ALCOVE 
        ], Preset::PSYCHOTIC + 1) 
      end 
  
      module Factory 
        Helper.define_enum(self, [ 
          :SMALLROOM, 
          :SHORTPASSAGE, 
          :MEDIUMROOM, 
          :LARGEROOM, 
          :LONGPASSAGE, 
          :HALL, 
          :CUPBOARD, 
          :COURTYARD, 
          :ALCOVE 
        ], Castle::ALCOVE + 1) 
      end 
  
      module IcePalace 
        Helper.define_enum(self, [ 
          :SMALLROOM, 
          :SHORTPASSAGE, 
          :MEDIUMROOM, 
          :LARGEROOM, 
          :LONGPASSAGE, 
          :HALL, 
          :CUPBOARD, 
          :COURTYARD, 
          :ALCOVE 
        ], Factory::ALCOVE + 1) 
      end 
  
      module SpaceStation 
        Helper.define_enum(self, [ 
          :SMALLROOM, 
          :SHORTPASSAGE, 
          :MEDIUMROOM, 
          :LARGEROOM, 
          :LONGPASSAGE, 
          :HALL, 
          :CUPBOARD, 
          :ALCOVE 
        ], IcePalace::ALCOVE + 1) 
      end 
  
      module WoodenGalleon 
        Helper.define_enum(self, [ 
          :SMALLROOM, 
          :SHORTPASSAGE, 
          :MEDIUMROOM, 
          :LARGEROOM, 
          :LONGPASSAGE, 
          :HALL, 
          :CUPBOARD, 
          :COURTYARD, 
          :ALCOVE 
        ], SpaceStation::ALCOVE + 1) 
      end 
  
      module Sports 
        Helper.define_enum(self, [ 
          :EMPTYSTADIUM, 
          :SQUASHCOURT, 
          :SMALLSWIMMINGPOOL, 
          :LARGESWIMMINGPOOL, 
          :GYMNASIUM, 
          :FULLSTADIUM, 
          :STADIUMTANNOY 
        ], WoodenGalleon::ALCOVE + 1) 
      end 
  
      module Prefab 
        Helper.define_enum(self, [ 
          :WORKSHOP, 
          :SCHOOLROOM, 
          :PRACTISEROOM, 
          :OUTHOUSE, 
          :CARAVAN 
        ], Sports::STADIUMTANNOY + 1) 
      end 
  
      module Dome 
        Helper.define_enum(self, [ 
          :TOMB, 
          :SAINTPAULS 
        ], Prefab::CARAVAN + 1) 
      end 
  
      module Pipe 
        Helper.define_enum(self, [ 
          :SMALL, 
          :LONGTHIN, 
          :LARGE, 
          :RESONANT 
        ], Dome::SAINTPAULS + 1) 
      end 
  
      module Outdoors 
        Helper.define_enum(self, [ 
          :BACKYARD, 
          :ROLLINGPLAINS, 
          :DEEPCANYON, 
          :CREEK, 
          :VALLEY 
        ], Pipe::RESONANT + 1) 
      end 
  
      module Mood 
        Helper.define_enum(self, [ 
          :HEAVEN, 
          :HELL, 
          :MEMORY 
        ], Outdoors::VALLEY + 1) 
      end 
  
      module Driving 
        Helper.define_enum(self, [ 
          :COMMENTATOR, 
          :PITGARAGE, 
          :INCAR_RACER, 
          :INCAR_SPORTS, 
          :INCAR_LUXURY, 
          :FULLGRANDSTAND, 
          :EMPTYGRANDSTAND, 
          :TUNNEL 
        ], Mood::MEMORY + 1) 
      end 
  
      module City 
        Helper.define_enum(self, [ 
          :STREETS, 
          :SUBWAY, 
          :MUSEUM, 
          :LIBRARY, 
          :UNDERPASS, 
          :ABANDONED 
        ], Driving::TUNNEL + 1) 
      end 
  
      module Misc 
        Helper.define_enum(self, [ 
          :DUSTYROOM, 
          :CHAPEL, 
          :SMALLWATERROOM 
        ], City::ABANDONED + 1) 
      end 
    end 
  end 
  
  class Source 
    include Helper 
  
    INIT = SealAPI.new('init_src', 'p') 
    DESTROY = SealAPI.new('destroy_src', 'p') 
    PLAY = SealAPI.new('play_src', 'p') 
    STOP = SealAPI.new('stop_src', 'p') 
    REWIND = SealAPI.new('rewind_src', 'p') 
    PAUSE = SealAPI.new('pause_src', 'p') 
    DETACH = SealAPI.new('detach_src_audio', 'p') 
    SET_BUF = SealAPI.new('set_src_buf', 'pp') 
    SET_STREAM = SealAPI.new('set_src_stream', 'pp') 
    FEED_EFS = SealAPI.new('feed_efs', 'ppi') 
    UPDATE = SealAPI.new('update_src', 'p') 
    SET_POS = SealAPI.new('set_src_pos', 'piii') 
    SET_VEL = SealAPI.new('set_src_vel', 'piii') 
    SET_GAIN = SealAPI.new('set_src_gain', 'pi') 
    SET_PITCH = SealAPI.new('set_src_pitch', 'pi') 
    SET_AUTO = SealAPI.new('set_src_auto', 'pi') 
    SET_RELATIVE = SealAPI.new('set_src_relative', 'pi') 
    SET_LOOPING = SealAPI.new('set_src_looping', 'pi') 
    SET_QUEUE_SIZE = SealAPI.new('set_src_queue_size', 'pi') 
    SET_CHUNK_SIZE = SealAPI.new('set_src_chunk_size', 'pi') 
    GET_POS = SealAPI.new('get_src_pos', 'pppp') 
    GET_VEL = SealAPI.new('get_src_vel', 'pppp') 
    GET_GAIN = SealAPI.new('get_src_gain', 'pp') 
    GET_PITCH = SealAPI.new('get_src_pitch', 'pp') 
    GET_AUTO = SealAPI.new('is_src_auto', 'pp') 
    GET_RELATIVE = SealAPI.new('is_src_relative', 'pp') 
    GET_LOOPING = SealAPI.new('is_src_looping', 'pp') 
    GET_QUEUE_SIZE = SealAPI.new('get_src_queue_size', 'pp') 
    GET_CHUNK_SIZE = SealAPI.new('get_src_chunk_size', 'pp') 
    GET_TYPE = SealAPI.new('get_src_type', 'pp') 
    GET_STATE = SealAPI.new('get_src_state', 'pp') 
  
    def initialize 
      [url=home.php?mod=space&uid=171370]@source[/url] = '    ' * 5 
      check_error(INIT[@source]) 
      ObjectSpace.define_finalizer(self, Helper.free(@source, DESTROY)) 
      self 
    end 
  
    def play 
      operate(PLAY) 
    end 
  
    def stop 
      operate(STOP) 
    end 
  
    def rewind 
      operate(REWIND) 
    end 
  
    def pause 
      operate(PAUSE) 
    end 
  
    def buffer=(buffer) 
      set_audio(:@buffer, buffer, SET_BUF) 
    end 
  
    def stream=(stream) 
      set_audio(:@stream, stream, SET_STREAM) 
    end 
  
    attr_reader :buffer, :stream 
  
    def feed(effect_slot, index) 
      native_efs_obj = effect_slot.instance_variable_get(:@effect_slot) 
      check_error(FEED_EFS[@source, native_efs_obj, index]) 
      self 
    end 
  
    def update 
      operate(UPDATE) 
    end 
  
    def position=(position) 
      set_3float(position, SET_POS) 
    end 
  
    def velocity=(velocity) 
      set_3float(velocity, SET_VEL) 
    end 
  
    def gain=(gain) 
      set_obj_float(@source, gain, SET_GAIN) 
    end 
  
    def pitch=(pitch) 
      set_obj_float(@source, pitch, SET_PITCH) 
    end 
  
    def auto=(auto) 
      set_obj_char(@source, auto, SET_AUTO) 
    end 
  
    def queue_size=(queue_size) 
      set_obj_int(@source, queue_size, SET_QUEUE_SIZE) 
    end 
  
    def chunk_size=(chunk_size) 
      set_obj_int(@source, chunk_size, SET_CHUNK_SIZE) 
    end 
  
    def relative=(relative) 
      set_obj_char(@source, relative, SET_RELATIVE) 
    end 
  
    def looping=(looping) 
      set_obj_char(@source, looping, SET_LOOPING) 
    end 
  
    def position 
      get_3float(GET_POS) 
    end 
  
    def velocity 
      get_3float(GET_VEL) 
    end 
  
    def gain 
      get_obj_float(@source, GET_GAIN) 
    end 
  
    def pitch 
      get_obj_float(@source, GET_PITCH) 
    end 
  
    def auto 
      get_obj_char(@source, GET_AUTO) 
    end 
  
    def relative 
      get_obj_char(@source, GET_RELATIVE) 
    end 
  
    def looping 
      get_obj_char(@source, GET_LOOPING) 
    end 
  
    alias auto? auto 
    alias relative? relative 
    alias looping? looping 
  
    def queue_size 
      get_obj_int(@source, GET_QUEUE_SIZE) 
    end 
  
    def chunk_size 
      get_obj_int(@source, GET_CHUNK_SIZE) 
    end 
  
    def type 
      case get_obj_int(@source, GET_TYPE) 
      when Type::STATIC 
        Type::STATIC 
      when Type::STREAMING 
        Type::STREAMING 
      else 
        Type::UNDETERMINED 
      end 
    end 
  
    def state 
      case get_obj_int(@source, GET_STATE) 
      when State::PLAYING 
        State::PLAYING 
      when State::PAUSED 
        State::PAUSED 
      when State::STOPPED 
        State::STOPPED 
      else 
        State::INITIAL 
      end 
    end 
  
  private 
    def operate(operation) 
      check_error(operation[@source]) 
      self 
    end 
  
    def set_audio(var, audio, setter) 
      if audio.nil? 
        operate(DETACH) 
      else 
        check_error(setter[@source, audio.instance_variable_get(var)]) 
      end 
      instance_variable_set(var, audio) 
      audio 
    end 
  
    def set_3float(float_tuple, setter) 
      integer_tuple = float_tuple.pack('f*').unpack('i*') 
      check_error(setter[@source, *integer_tuple]) 
      float_tuple 
    end 
  
    def get_3float(getter) 
      float_tuple_buffers = Array.new(3) { '    ' } 
      check_error(getter[@source, *float_tuple_buffers]) 
      float_tuple_buffers.join.unpack('f*') 
    end 
  
    module State 
      Helper.define_enum(self, [ 
        :INITIAL, 
        :PLAYING, 
        :PAUSED, 
        :STOPPED 
      ]) 
    end 
  
    module Type 
      Helper.define_enum(self, [ 
        :UNDETERMINED, 
        :STATIC, 
        :STREAMING 
      ]) 
    end 
  end 
  
  class EffectSlot 
    include Helper 
  
    INIT = SealAPI.new('init_efs', 'p') 
    DESTROY = SealAPI.new('destroy_efs', 'p') 
    SET_EFFECT = SealAPI.new('set_efs_effect', 'pp') 
    SET_GAIN = SealAPI.new('set_efs_gain', 'pi') 
    SET_AUTO = SealAPI.new('set_efs_auto', 'pi') 
    GET_GAIN = SealAPI.new('get_efs_gain', 'pp') 
    GET_AUTO = SealAPI.new('is_efs_auto', 'pp') 
  
    def initialize(effect = nil) 
      @effect_slot = '    ' 
      check_error(INIT[@effect_slot]) 
      self.effect = effect if effect 
      ObjectSpace.define_finalizer(self, Helper.free(@effect_slot, DESTROY)) 
      self 
    end 
  
    def effect=(effect) 
      native_effect_obj = effect ? effect.instance_variable_get(:@effect) : 0 
      check_error(SET_EFFECT[@effect_slot, native_effect_obj]) 
      @effect = effect 
      effect 
    end 
  
    attr_reader :effect 
  
    def gain=(gain) 
      set_obj_float(@effect_slot, gain, SET_GAIN) 
    end 
  
    def gain 
      get_obj_float(@effect_slot, GET_GAIN) 
    end 
  
    def auto=(auto) 
      set_obj_char(@effect_slot, auto, SET_AUTO) 
    end 
  
    def auto 
      get_obj_char(@effect_slot, GET_AUTO) 
    end 
  
    alias auto? auto 
  end 
end 
 Seal 的 C 庫:
 
  seal.zip
(243.88 KB, 下载次数: 151) 
 (以防萬一以上壓縮包也包含了 Win32API 綁定的 Ruby 源文件,可以直接粘貼入腳本編輯器)
 
 非 Ruby 程序
 
 可以直接將 Seal 編譯為 C 庫並調用(其實就是上面提供的 seal.dll):
 https://github.com/zhangsu/seal#msvc--microsoft-visual-studio-2010
 
 Ruby 程序
 
 可以直接使用 gem:
 https://rubygems.org/gems/seal
 
 目前正和@叶子 趕製一款小遊戲演示 Seal 的音效功能,預計這幾天發佈。
 
 待完成功能
 
 
 Source 屬性
 最遠減淡距離滾降因子參考距離最大、最小音量方向矢量與錐形音量範圍當前播放位置
改變 OpenAL 距離模型音速多普勒因子輸出設備枚舉音頻輸入(錄製)特效
前奏對象克隆(Object#clone/Object#dup)
 
 貢獻代碼
 
 目前的開發是在 Github 上:https://github.com/zhangsu/seal
 強烈歡迎提交 pull request,大小不論,哪怕只是修復一個錯字。
 | 
 评分
查看全部评分
 |