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