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