plaything 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ -Ilib
2
+ --color
3
+ --require ./spec/spec_helper
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "pry"
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,7 @@
1
+ require "ffi"
2
+
3
+ class FFI::AbstractMemory
4
+ def count
5
+ size.div(type_size)
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ require "plaything/objects/device"
2
+ require "plaything/objects/context"
3
+ require "plaything/objects/source"
4
+ require "plaything/objects/buffer"
@@ -0,0 +1,7 @@
1
+ class Plaything
2
+ module OpenAL
3
+ class Buffer < TypeClass(FFI::Type::UINT)
4
+ include OpenAL::Paramable(:buffer)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ class Plaything
2
+ module OpenAL
3
+ class Context < ManagedPointer
4
+ def self.release(context)
5
+ super do |pointer|
6
+ OpenAL.make_context_current(nil)
7
+ OpenAL.destroy_context(context)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ class Plaything
2
+ module OpenAL
3
+ class Device < ManagedPointer
4
+ def self.release(device)
5
+ super { |pointer| OpenAL.close_device(device) }
6
+ end
7
+ end
8
+ end
9
+ 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,3 @@
1
+ require "plaything/support/managed_pointer"
2
+ require "plaything/support/type_class"
3
+ require "plaything/support/paramable"
@@ -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
+
@@ -0,0 +1,3 @@
1
+ class Plaything
2
+ VERSION = "1.0.0"
3
+ end
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
@@ -0,0 +1,5 @@
1
+ describe "Plaything" do
2
+ specify "VERSION is defined" do
3
+ defined?(Plaything::VERSION).should eq "constant"
4
+ end
5
+ end
@@ -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