plaything 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/Gemfile +5 -0
- data/README.md +28 -0
- data/Rakefile +14 -0
- data/lib/plaything.rb +147 -0
- data/lib/plaything/monkey_patches/ffi.rb +7 -0
- data/lib/plaything/objects.rb +4 -0
- data/lib/plaything/objects/buffer.rb +7 -0
- data/lib/plaything/objects/context.rb +12 -0
- data/lib/plaything/objects/device.rb +9 -0
- data/lib/plaything/objects/source.rb +30 -0
- data/lib/plaything/openal.rb +164 -0
- data/lib/plaything/support.rb +3 -0
- data/lib/plaything/support/managed_pointer.rb +26 -0
- data/lib/plaything/support/paramable.rb +42 -0
- data/lib/plaything/support/type_class.rb +53 -0
- data/lib/plaything/version.rb +3 -0
- data/plaything.gemspec +24 -0
- data/spec/plaything_spec.rb +5 -0
- data/spec/spec_helper.rb +1 -0
- metadata +120 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Plaything
|
2
|
+
|
3
|
+
Blast raw PCM audio through your speakers using OpenAL.
|
4
|
+
|
5
|
+
## License
|
6
|
+
|
7
|
+
Copyright (c) 2013 Kim Burgestrand
|
8
|
+
|
9
|
+
MIT License
|
10
|
+
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
12
|
+
a copy of this software and associated documentation files (the
|
13
|
+
"Software"), to deal in the Software without restriction, including
|
14
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
15
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
16
|
+
permit persons to whom the Software is furnished to do so, subject to
|
17
|
+
the following conditions:
|
18
|
+
|
19
|
+
The above copyright notice and this permission notice shall be
|
20
|
+
included in all copies or substantial portions of the Software.
|
21
|
+
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
23
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
24
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
25
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
26
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
27
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
28
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
begin
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
task :console do
|
5
|
+
exec "pry", "-rbundler/setup", "-rplaything"
|
6
|
+
end
|
7
|
+
rescue LoadError
|
8
|
+
# don't need bundler for dev
|
9
|
+
end
|
10
|
+
|
11
|
+
require "rspec/core/rake_task"
|
12
|
+
RSpec::Core::RakeTask.new(:default) do |spec|
|
13
|
+
spec.ruby_opts = %w[-W]
|
14
|
+
end
|
data/lib/plaything.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
require "ffi"
|
2
|
+
require "plaything/version"
|
3
|
+
require "plaything/monkey_patches/ffi"
|
4
|
+
require "plaything/support"
|
5
|
+
require "plaything/objects"
|
6
|
+
require "plaything/openal"
|
7
|
+
|
8
|
+
class Plaything
|
9
|
+
Error = Class.new(StandardError)
|
10
|
+
|
11
|
+
# Open the default output device and prepare it for playback.
|
12
|
+
#
|
13
|
+
# @param [Hash] options
|
14
|
+
# @option options [Symbol] sample_type (:int16)
|
15
|
+
# @option options [Integer] sample_rate (44100)
|
16
|
+
# @option options [Integer] channels (2)
|
17
|
+
def initialize(options = { sample_type: :int16, sample_rate: 44100, channels: 2 })
|
18
|
+
@device = OpenAL.open_device(nil)
|
19
|
+
raise Error, "Failed to open device" if @device.null?
|
20
|
+
|
21
|
+
@context = OpenAL.create_context(@device, nil)
|
22
|
+
OpenAL.make_context_current(@context)
|
23
|
+
OpenAL.distance_model(:none)
|
24
|
+
OpenAL.listenerf(:gain, 1.0)
|
25
|
+
|
26
|
+
FFI::MemoryPointer.new(OpenAL::Source, 1) do |ptr|
|
27
|
+
OpenAL.gen_sources(ptr.count, ptr)
|
28
|
+
@source = OpenAL::Source.new(ptr.read_uint)
|
29
|
+
end
|
30
|
+
|
31
|
+
@sample_type = options.fetch(:sample_type)
|
32
|
+
@sample_rate = Integer(options.fetch(:sample_rate))
|
33
|
+
@channels = Integer(options.fetch(:channels))
|
34
|
+
|
35
|
+
@sample_format = { [ :int16, 2 ] => :stereo16, }.fetch([@sample_type, @channels]) do
|
36
|
+
raise TypeError, "unknown sample format for type [#{@sample_type}, #{@channels}]"
|
37
|
+
end
|
38
|
+
|
39
|
+
FFI::MemoryPointer.new(OpenAL::Buffer, 3) do |ptr|
|
40
|
+
OpenAL.gen_buffers(ptr.count, ptr)
|
41
|
+
@buffers = OpenAL::Buffer.extract(ptr, ptr.count)
|
42
|
+
end
|
43
|
+
|
44
|
+
@free_buffers = @buffers.clone
|
45
|
+
@queued_buffers = []
|
46
|
+
@queued_frames = []
|
47
|
+
|
48
|
+
# 44100 int16s = 22050 frames = 0.5s (1 frame * 2 channels = 2 int16 = 1 sample = 1/44100 s)
|
49
|
+
@buffer_size = @sample_rate * @channels * 1.0
|
50
|
+
# how many samples there are in each buffer, irrespective of channels
|
51
|
+
@buffer_length = @buffer_size / @channels
|
52
|
+
# buffer_duration = buffer_length / sample_rate
|
53
|
+
|
54
|
+
@total_buffers_processed = 0
|
55
|
+
end
|
56
|
+
|
57
|
+
# Start playback of queued audio.
|
58
|
+
#
|
59
|
+
# @note You must continue to supply audio, or playback will cease.
|
60
|
+
def play
|
61
|
+
OpenAL.source_play(@source)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Pause playback of queued audio. Playback will resume from current position when {#play} is called.
|
65
|
+
def pause
|
66
|
+
OpenAL.source_pause(@source)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Stop playback and clear any queued audio.
|
70
|
+
#
|
71
|
+
# @note All audio queues are completely cleared, and {#position} is reset.
|
72
|
+
def stop
|
73
|
+
OpenAL.source_stop(@source)
|
74
|
+
@source.detach_buffers
|
75
|
+
@free_buffers.concat(@queued_buffers)
|
76
|
+
@queued_buffers.clear
|
77
|
+
@queued_frames.clear
|
78
|
+
@total_buffers_processed = 0
|
79
|
+
end
|
80
|
+
|
81
|
+
# @return [Rational] how many seconds of audio that has been played.
|
82
|
+
def position
|
83
|
+
Rational(@total_buffers_processed * @buffer_length + sample_offset, @sample_rate)
|
84
|
+
end
|
85
|
+
|
86
|
+
# @return [Integer] total size of current play queue.
|
87
|
+
def queue_size
|
88
|
+
@source.get(:buffers_queued, Integer) * @buffer_length - sample_offset
|
89
|
+
end
|
90
|
+
|
91
|
+
# @return [Integer] how many audio drops since last call to drops.
|
92
|
+
def drops
|
93
|
+
0
|
94
|
+
end
|
95
|
+
|
96
|
+
# Queue audio frames for playback.
|
97
|
+
#
|
98
|
+
# @param [Array<[ Channels… ]>] frames array of N-sized arrays of integers.
|
99
|
+
def <<(frames)
|
100
|
+
if buffers_processed > 0
|
101
|
+
FFI::MemoryPointer.new(OpenAL::Buffer, buffers_processed) do |ptr|
|
102
|
+
OpenAL.source_unqueue_buffers(@source, ptr.count, ptr)
|
103
|
+
@total_buffers_processed += ptr.count
|
104
|
+
@free_buffers.concat OpenAL::Buffer.extract(ptr, ptr.count)
|
105
|
+
@queued_buffers.delete_if { |buffer| @free_buffers.include?(buffer) }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
wanted_size = (@buffer_size - @queued_frames.length).div(@channels) * @channels
|
110
|
+
consumed_frames = frames.take(wanted_size)
|
111
|
+
@queued_frames.concat(consumed_frames)
|
112
|
+
|
113
|
+
if @queued_frames.length >= @buffer_size and @free_buffers.any?
|
114
|
+
current_buffer = @free_buffers.shift
|
115
|
+
|
116
|
+
FFI::MemoryPointer.new(@sample_type, @queued_frames.length) do |frames|
|
117
|
+
frames.public_send(:"write_array_of_#{@sample_type}", @queued_frames)
|
118
|
+
# stereo16 = 2 int16s (1 frame) = 1 sample
|
119
|
+
OpenAL.buffer_data(current_buffer, @sample_format, frames, frames.size, @sample_rate)
|
120
|
+
@queued_frames.clear
|
121
|
+
end
|
122
|
+
|
123
|
+
FFI::MemoryPointer.new(OpenAL::Buffer, 1) do |buffers|
|
124
|
+
buffers.write_uint(current_buffer.to_native)
|
125
|
+
OpenAL.source_queue_buffers(@source, buffers.count, buffers)
|
126
|
+
end
|
127
|
+
|
128
|
+
@queued_buffers.push(current_buffer)
|
129
|
+
end
|
130
|
+
|
131
|
+
consumed_frames.length
|
132
|
+
end
|
133
|
+
|
134
|
+
protected
|
135
|
+
|
136
|
+
def sample_offset
|
137
|
+
@source.get(:sample_offset, Integer)
|
138
|
+
end
|
139
|
+
|
140
|
+
def buffers_processed
|
141
|
+
if not @source.stopped?
|
142
|
+
@source.get(:buffers_processed, Integer)
|
143
|
+
else
|
144
|
+
0
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Plaything
|
2
|
+
module OpenAL
|
3
|
+
class Source < TypeClass(FFI::Type::UINT)
|
4
|
+
include OpenAL::Paramable(:source)
|
5
|
+
|
6
|
+
# Detach all queued or attached buffers.
|
7
|
+
#
|
8
|
+
# @note all buffers must be processed for this operation to succeed.
|
9
|
+
# @note all buffers are considered processed when {#stopped?}.
|
10
|
+
def detach_buffers
|
11
|
+
set(:buffer, 0)
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Symbol] :initial, :paused, :playing, :stopped
|
15
|
+
def state
|
16
|
+
get(:source_state)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Boolean] true if source is in stopped state.
|
20
|
+
def stopped?
|
21
|
+
state == :stopped
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Boolean] true if source is in stopped state.
|
25
|
+
def playing?
|
26
|
+
state == :playing
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
class Plaything
|
2
|
+
module OpenAL
|
3
|
+
extend FFI::Library
|
4
|
+
|
5
|
+
ffi_lib ["openal", "/System/Library/Frameworks/OpenAL.framework/Versions/Current/OpenAL"]
|
6
|
+
|
7
|
+
typedef :pointer, :attributes
|
8
|
+
typedef :int, :sizei
|
9
|
+
|
10
|
+
# Errors
|
11
|
+
Error = Class.new(StandardError)
|
12
|
+
|
13
|
+
enum :error, [
|
14
|
+
:no_error, 0,
|
15
|
+
:invalid_name, 0xA001,
|
16
|
+
:invalid_enum, 0xA002,
|
17
|
+
:invalid_value, 0xA003,
|
18
|
+
:invalid_operation, 0xA004,
|
19
|
+
:out_of_memory, 0xA005,
|
20
|
+
]
|
21
|
+
attach_function :get_error, :alGetError, [ ], :error
|
22
|
+
|
23
|
+
# Overridden for three purposes.
|
24
|
+
#
|
25
|
+
# 1. Allows us to only supply OpenAL name, and converts it to snake_case
|
26
|
+
# for attaching the function.
|
27
|
+
# 2. Wraps the call in an error-raise checker.
|
28
|
+
# 3. Creates a bang method that does not do automatic error checking.
|
29
|
+
def self.attach_function(c_name, params, returns, options = {})
|
30
|
+
ruby_name = c_name
|
31
|
+
.to_s
|
32
|
+
.sub(/\Aalc?/, "")
|
33
|
+
.gsub(/(?<!\A)\p{Lu}/u, '_\0')
|
34
|
+
.downcase
|
35
|
+
bang_name = "#{ruby_name}!"
|
36
|
+
|
37
|
+
super(ruby_name, c_name, params, returns, options)
|
38
|
+
alias_method(bang_name, ruby_name)
|
39
|
+
|
40
|
+
define_method(ruby_name) do |*args, &block|
|
41
|
+
get_error # clear error
|
42
|
+
public_send(bang_name, *args, &block).tap do
|
43
|
+
error = get_error
|
44
|
+
unless error == :no_error
|
45
|
+
raise Error, "#{ruby_name} failed with #{error}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module_function ruby_name
|
51
|
+
module_function bang_name
|
52
|
+
end
|
53
|
+
|
54
|
+
# Devices
|
55
|
+
attach_function :alcOpenDevice, [ :string ], Device
|
56
|
+
attach_function :alcCloseDevice, [ Device ], :bool
|
57
|
+
|
58
|
+
# Context
|
59
|
+
attach_function :alcCreateContext, [ Device, :attributes ], Context
|
60
|
+
attach_function :alcDestroyContext, [ Context ], :void
|
61
|
+
attach_function :alcMakeContextCurrent, [ Context ], :bool
|
62
|
+
|
63
|
+
# Sources
|
64
|
+
attach_function :alGenSources, [ :sizei, :pointer ], :void
|
65
|
+
attach_function :alDeleteSources, [ :sizei, :pointer ], :void
|
66
|
+
|
67
|
+
attach_function :alSourcePlay, [ Source ], :void
|
68
|
+
attach_function :alSourcePause, [ Source ], :void
|
69
|
+
attach_function :alSourceStop, [ Source ], :void
|
70
|
+
|
71
|
+
attach_function :alSourceQueueBuffers, [ Source, :sizei, :pointer ], :void
|
72
|
+
attach_function :alSourceUnqueueBuffers, [ Source, :sizei, :pointer ], :void
|
73
|
+
|
74
|
+
# Buffers
|
75
|
+
enum :format, [
|
76
|
+
:mono8, 0x1100,
|
77
|
+
:mono16, 0x1101,
|
78
|
+
:stereo8, 0x1102,
|
79
|
+
:stereo16, 0x1103,
|
80
|
+
]
|
81
|
+
attach_function :alGenBuffers, [ :sizei, :pointer ], :void
|
82
|
+
attach_function :alDeleteBuffers, [ :sizei, :pointer ], :void
|
83
|
+
|
84
|
+
attach_function :alBufferData, [ Buffer, :format, :pointer, :sizei, :sizei ], :void
|
85
|
+
|
86
|
+
# Parameters
|
87
|
+
enum :parameter, [
|
88
|
+
:none, 0x0000,
|
89
|
+
|
90
|
+
:source_relative, 0x0202,
|
91
|
+
|
92
|
+
:cone_inner_angle, 0x1001,
|
93
|
+
:cone_outer_angle, 0x1002,
|
94
|
+
:pitch, 0x1003,
|
95
|
+
:position, 0x1004,
|
96
|
+
:direction, 0x1005,
|
97
|
+
:velocity, 0x1006,
|
98
|
+
:looping, 0x1007,
|
99
|
+
:buffer, 0x1009,
|
100
|
+
:gain, 0x100A,
|
101
|
+
:min_gain, 0x100D,
|
102
|
+
:max_gain, 0x100E,
|
103
|
+
:orientation, 0x100F,
|
104
|
+
|
105
|
+
:source_state, 0x1010,
|
106
|
+
:initial, 0x1011,
|
107
|
+
:playing, 0x1012,
|
108
|
+
:paused, 0x1013,
|
109
|
+
:stopped, 0x1014,
|
110
|
+
:buffers_queued, 0x1015,
|
111
|
+
:buffers_processed, 0x1016,
|
112
|
+
|
113
|
+
:reference_distance, 0x1020,
|
114
|
+
:rolloff_factor, 0x1021,
|
115
|
+
:cone_outer_gain, 0x1022,
|
116
|
+
:max_distance, 0x1023,
|
117
|
+
:sec_offset, 0x1024,
|
118
|
+
:sample_offset, 0x1025,
|
119
|
+
:byte_offset, 0x1026,
|
120
|
+
:source_type, 0x1027,
|
121
|
+
|
122
|
+
:frequency, 0x2001,
|
123
|
+
:bits, 0x2002,
|
124
|
+
:channels, 0x2003,
|
125
|
+
:size, 0x2004,
|
126
|
+
:unused, 0x2010,
|
127
|
+
:pending, 0x2011,
|
128
|
+
:processed, 0x2012,
|
129
|
+
|
130
|
+
:distance_model, 0xD000,
|
131
|
+
:inverse_distance, 0xD001,
|
132
|
+
:inverse_distance_clamped, 0xD002,
|
133
|
+
:linear_distance, 0xD003,
|
134
|
+
:linear_distance_clamped, 0xD004,
|
135
|
+
:exponent_distance, 0xD005,
|
136
|
+
:exponent_distance_clamped, 0xD006,
|
137
|
+
]
|
138
|
+
|
139
|
+
## Utility
|
140
|
+
attach_function :alGetEnumValue, [ :string ], :int
|
141
|
+
|
142
|
+
enum_type(:parameter).to_h.each do |name, value|
|
143
|
+
real_name = "AL_#{name.to_s.upcase}"
|
144
|
+
real_value = get_enum_value(real_name)
|
145
|
+
if real_value != -1 && value != real_value
|
146
|
+
raise NameError, "#{name} has value #{value}, should be #{real_value}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
## Listeners
|
151
|
+
attach_function :alListenerf, [ :parameter, :float ], :void
|
152
|
+
|
153
|
+
## Sources
|
154
|
+
attach_function :alSourcei, [ Source, :parameter, :int ], :void
|
155
|
+
attach_function :alGetSourcei, [ Source, :parameter, :pointer ], :void
|
156
|
+
|
157
|
+
## Sources
|
158
|
+
attach_function :alBufferi, [ Buffer, :parameter, :int ], :void
|
159
|
+
attach_function :alGetBufferi, [ Buffer, :parameter, :pointer ], :void
|
160
|
+
|
161
|
+
# Global params
|
162
|
+
attach_function :alDistanceModel, [ :parameter ], :void
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Plaything
|
2
|
+
module OpenAL
|
3
|
+
class ManagedPointer < FFI::AutoPointer
|
4
|
+
class << self
|
5
|
+
def release(pointer)
|
6
|
+
if pointer.null?
|
7
|
+
warn "Trying to release NULL #{name}."
|
8
|
+
elsif block_given?
|
9
|
+
yield pointer
|
10
|
+
else
|
11
|
+
warn "No releaser for #{name}."
|
12
|
+
end
|
13
|
+
rescue => e
|
14
|
+
warn "release for #{name} failed: #{e.message}."
|
15
|
+
end
|
16
|
+
|
17
|
+
def allocate(*args, &block)
|
18
|
+
pointer = FFI::MemoryPointer.new(*args)
|
19
|
+
yield pointer
|
20
|
+
pointer.autorelease = false
|
21
|
+
new(FFI::Pointer.new(pointer))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class Plaything
|
2
|
+
module OpenAL
|
3
|
+
def self.Paramable(type)
|
4
|
+
Module.new do
|
5
|
+
define_method(:al_type) do
|
6
|
+
type
|
7
|
+
end
|
8
|
+
|
9
|
+
def set(parameter, value)
|
10
|
+
type = if value.is_a?(Integer)
|
11
|
+
OpenAL.public_send(:"#{al_type}i", self, parameter, value)
|
12
|
+
elsif value.is_a?(Float)
|
13
|
+
OpenAL.public_send(:"#{al_type}f", self, parameter, value)
|
14
|
+
else
|
15
|
+
raise TypeError, "invalid type of #{value}, must be int or float"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def get(parameter, type = :enum)
|
20
|
+
name = if type == Integer
|
21
|
+
:i
|
22
|
+
elsif type == Float
|
23
|
+
:f
|
24
|
+
elsif type == :enum
|
25
|
+
:i
|
26
|
+
else
|
27
|
+
raise TypeError, "unknown type #{type}"
|
28
|
+
end
|
29
|
+
|
30
|
+
reader = { f: :float, i: :int }.fetch(name)
|
31
|
+
|
32
|
+
FFI::MemoryPointer.new(reader) do |ptr|
|
33
|
+
OpenAL.public_send(:"get_#{al_type}#{name}", self, parameter, ptr)
|
34
|
+
value = ptr.public_send(:"read_#{reader}")
|
35
|
+
value = OpenAL.enum_type(:parameter)[value] if type == :enum
|
36
|
+
return value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class Plaything
|
2
|
+
module OpenAL
|
3
|
+
def self.TypeClass(type)
|
4
|
+
Class.new do
|
5
|
+
extend FFI::DataConverter
|
6
|
+
@@type = type
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def inherited(other)
|
10
|
+
other.native_type(type)
|
11
|
+
end
|
12
|
+
|
13
|
+
def type
|
14
|
+
@@type
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_native(source, ctx)
|
18
|
+
source.value
|
19
|
+
end
|
20
|
+
|
21
|
+
def from_native(value, ctx)
|
22
|
+
new(value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def size
|
26
|
+
type.size
|
27
|
+
end
|
28
|
+
|
29
|
+
def extract(pointer, count)
|
30
|
+
pointer.read_array_of_type(self, :read_uint, count).map do |uint|
|
31
|
+
new(uint)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(value)
|
37
|
+
@value = value
|
38
|
+
end
|
39
|
+
|
40
|
+
def ==(other)
|
41
|
+
other.is_a?(self.class) and other.value == value
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_native
|
45
|
+
self.class.to_native(self, nil)
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
data/plaything.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require File.expand_path("./lib/plaything/version", File.dirname(__FILE__))
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = "plaything"
|
6
|
+
spec.summary = "Blast raw PCM audio through your speakers using OpenAL."
|
7
|
+
|
8
|
+
spec.homepage = "https://github.com/Burgestrand/plaything"
|
9
|
+
spec.authors = ["Kim Burgestrand"]
|
10
|
+
spec.email = ["kim@burgestrand.se"]
|
11
|
+
spec.license = "MIT"
|
12
|
+
|
13
|
+
spec.files = `git ls-files`.split($/)
|
14
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
15
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
16
|
+
spec.require_paths = ["lib"]
|
17
|
+
|
18
|
+
spec.version = Plaything::VERSION
|
19
|
+
spec.required_ruby_version = ">= 1.9"
|
20
|
+
|
21
|
+
spec.add_dependency "ffi", "~> 1.1"
|
22
|
+
spec.add_development_dependency "rspec"
|
23
|
+
spec.add_development_dependency "rake"
|
24
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "plaything"
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: plaything
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 1.0.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Kim Burgestrand
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-26 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
version_requirements: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.1'
|
20
|
+
none: false
|
21
|
+
prerelease: false
|
22
|
+
name: ffi
|
23
|
+
requirement: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.1'
|
28
|
+
none: false
|
29
|
+
type: :runtime
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
version_requirements: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ! '>='
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
none: false
|
37
|
+
prerelease: false
|
38
|
+
name: rspec
|
39
|
+
requirement: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
none: false
|
45
|
+
type: :development
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
version_requirements: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
none: false
|
53
|
+
prerelease: false
|
54
|
+
name: rake
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
none: false
|
61
|
+
type: :development
|
62
|
+
description:
|
63
|
+
email:
|
64
|
+
- kim@burgestrand.se
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- .rspec
|
71
|
+
- Gemfile
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- lib/plaything.rb
|
75
|
+
- lib/plaything/monkey_patches/ffi.rb
|
76
|
+
- lib/plaything/objects.rb
|
77
|
+
- lib/plaything/objects/buffer.rb
|
78
|
+
- lib/plaything/objects/context.rb
|
79
|
+
- lib/plaything/objects/device.rb
|
80
|
+
- lib/plaything/objects/source.rb
|
81
|
+
- lib/plaything/openal.rb
|
82
|
+
- lib/plaything/support.rb
|
83
|
+
- lib/plaything/support/managed_pointer.rb
|
84
|
+
- lib/plaything/support/paramable.rb
|
85
|
+
- lib/plaything/support/type_class.rb
|
86
|
+
- lib/plaything/version.rb
|
87
|
+
- plaything.gemspec
|
88
|
+
- spec/plaything_spec.rb
|
89
|
+
- spec/spec_helper.rb
|
90
|
+
homepage: https://github.com/Burgestrand/plaything
|
91
|
+
licenses:
|
92
|
+
- MIT
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '1.9'
|
102
|
+
none: false
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ! '>='
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
segments:
|
109
|
+
- 0
|
110
|
+
hash: 2964046639458012733
|
111
|
+
none: false
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 1.8.24
|
115
|
+
signing_key:
|
116
|
+
specification_version: 3
|
117
|
+
summary: Blast raw PCM audio through your speakers using OpenAL.
|
118
|
+
test_files:
|
119
|
+
- spec/plaything_spec.rb
|
120
|
+
- spec/spec_helper.rb
|