plaything 1.0.0 → 1.1.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.
- checksums.yaml +7 -0
- data/README.md +13 -1
- data/lib/plaything.rb +143 -66
- data/lib/plaything/objects/buffer.rb +4 -0
- data/lib/plaything/objects/source.rb +31 -0
- data/lib/plaything/openal.rb +8 -6
- data/lib/plaything/support/managed_pointer.rb +0 -7
- data/lib/plaything/support/type_class.rb +4 -11
- data/lib/plaything/version.rb +1 -1
- metadata +22 -33
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3b370e69436137efbd0bb9964231b74bae9863a7
|
4
|
+
data.tar.gz: e8d93ef8632e8191f2be628a58618f04723e5cf6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5026505b6b2650bbac2e3a644b10bdbbb78ff675a8c8b9d3c86ec9d33ab81ac5176b5596da5d117837546a99b6aadf4b455ff2630cdd53682c8a2e60cb4fb1bc
|
7
|
+
data.tar.gz: d5590baa1b903ea691266b1c38d9834ead39c9d669b01113f929055e730918afefbb4847ebbd73fd973b6e82016c4824662d9bd869dd4001107de3f11be488e0
|
data/README.md
CHANGED
@@ -1,6 +1,18 @@
|
|
1
1
|
# Plaything
|
2
2
|
|
3
|
-
|
3
|
+
> OpenAL is a cross-platform 3D audio API appropriate for use with gaming applications and many other types of audio applications.
|
4
|
+
|
5
|
+
Plaything is tiny API wrapper around OpenAL, and makes it easy to play raw (PCM) streaming audio through your speakers.
|
6
|
+
|
7
|
+
Plaything was initially written to support audio playback from the [spotify gem](http://rubygems.org/gems/spotify).
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
## Note about the OpenAL bindings
|
12
|
+
|
13
|
+
Plaything contains bindings to a subset of the OpenAL API, just enough to cover the necessary streaming functionality. With little further work, the OpenAL bindings could be extracted and further developed indepdendently of Plaything.
|
14
|
+
|
15
|
+
Additionally, the OpenAL streaming source is retrievable from Plaything, and allows you to modify parameters on the playback source, such as pitch, gain, and anything else OpenAL allows you to change.
|
4
16
|
|
5
17
|
## License
|
6
18
|
|
data/lib/plaything.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "monitor"
|
1
2
|
require "ffi"
|
2
3
|
require "plaything/version"
|
3
4
|
require "plaything/monkey_patches/ffi"
|
@@ -5,16 +6,33 @@ require "plaything/support"
|
|
5
6
|
require "plaything/objects"
|
6
7
|
require "plaything/openal"
|
7
8
|
|
9
|
+
# Plaything is tiny API wrapper around OpenAL, and makes it easy to play raw
|
10
|
+
# (PCM) streaming audio through your speakers.
|
11
|
+
#
|
12
|
+
# API consist of a few key methods available on the Plaything instance.
|
13
|
+
#
|
14
|
+
# - {#play}, {#pause}, {#stop} — controls source playback state. If the source
|
15
|
+
# runs out of audio to play, it will forcefully stop playback.
|
16
|
+
# - {#position}, can be used to retrieve playback position.
|
17
|
+
# - {#queue_size}, {#drops} — status information; should be used by the streaming
|
18
|
+
# source to improve playback experience.
|
19
|
+
# - {#format=} — allows you to change format, even during playback.
|
20
|
+
# - {#stream}, {#<<} — fills the audio buffers with PCM audio.
|
21
|
+
#
|
22
|
+
# Internally, Plaything will queue and unqueue buffers as they are played during
|
23
|
+
# streaming. When a sufficient amount of audio has been fed into plaything, the
|
24
|
+
# audio will be queued on the source and plaything can accept additional audio.
|
25
|
+
#
|
26
|
+
# Plaything is considered thread-safe.
|
8
27
|
class Plaything
|
9
28
|
Error = Class.new(StandardError)
|
29
|
+
Formats = {
|
30
|
+
[ :int16, 1 ] => :mono16,
|
31
|
+
[ :int16, 2 ] => :stereo16,
|
32
|
+
}
|
10
33
|
|
11
34
|
# 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 })
|
35
|
+
def initialize(format = { sample_rate: 44100, sample_type: :int16, channels: 2 })
|
18
36
|
@device = OpenAL.open_device(nil)
|
19
37
|
raise Error, "Failed to open device" if @device.null?
|
20
38
|
|
@@ -28,14 +46,6 @@ class Plaything
|
|
28
46
|
@source = OpenAL::Source.new(ptr.read_uint)
|
29
47
|
end
|
30
48
|
|
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
49
|
FFI::MemoryPointer.new(OpenAL::Buffer, 3) do |ptr|
|
40
50
|
OpenAL.gen_buffers(ptr.count, ptr)
|
41
51
|
@buffers = OpenAL::Buffer.extract(ptr, ptr.count)
|
@@ -45,103 +55,170 @@ class Plaything
|
|
45
55
|
@queued_buffers = []
|
46
56
|
@queued_frames = []
|
47
57
|
|
48
|
-
|
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
|
-
|
58
|
+
@drops = 0
|
54
59
|
@total_buffers_processed = 0
|
60
|
+
|
61
|
+
@monitor = Monitor.new
|
62
|
+
|
63
|
+
self.format = format
|
55
64
|
end
|
56
65
|
|
66
|
+
# @return [Plaything::OpenAL::Source] the back-end audio source.
|
67
|
+
attr_reader :source
|
68
|
+
|
57
69
|
# Start playback of queued audio.
|
58
70
|
#
|
59
71
|
# @note You must continue to supply audio, or playback will cease.
|
60
72
|
def play
|
61
|
-
|
73
|
+
synchronize { @source.play }
|
62
74
|
end
|
63
75
|
|
64
76
|
# Pause playback of queued audio. Playback will resume from current position when {#play} is called.
|
65
77
|
def pause
|
66
|
-
|
78
|
+
synchronize { @source.pause }
|
67
79
|
end
|
68
80
|
|
69
81
|
# Stop playback and clear any queued audio.
|
70
82
|
#
|
71
83
|
# @note All audio queues are completely cleared, and {#position} is reset.
|
72
84
|
def stop
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
85
|
+
synchronize do
|
86
|
+
@source.stop
|
87
|
+
@source.detach_buffers
|
88
|
+
@free_buffers.concat(@queued_buffers)
|
89
|
+
@queued_buffers.clear
|
90
|
+
@queued_frames.clear
|
91
|
+
@total_buffers_processed = 0
|
92
|
+
end
|
79
93
|
end
|
80
94
|
|
81
95
|
# @return [Rational] how many seconds of audio that has been played.
|
82
96
|
def position
|
83
|
-
|
97
|
+
synchronize do
|
98
|
+
total_samples_processed = @total_buffers_processed * @buffer_length
|
99
|
+
Rational(total_samples_processed + @source.sample_offset, @sample_rate)
|
100
|
+
end
|
84
101
|
end
|
85
102
|
|
86
103
|
# @return [Integer] total size of current play queue.
|
87
104
|
def queue_size
|
88
|
-
|
105
|
+
synchronize do
|
106
|
+
@source.buffers_queued * @buffer_length - @source.sample_offset
|
107
|
+
end
|
89
108
|
end
|
90
109
|
|
91
110
|
# @return [Integer] how many audio drops since last call to drops.
|
92
111
|
def drops
|
93
|
-
|
112
|
+
synchronize do
|
113
|
+
@drops.tap { @drops = 0 }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# @return [Hash] current audio format in the queues
|
118
|
+
def format
|
119
|
+
synchronize do
|
120
|
+
{
|
121
|
+
sample_rate: @sample_rate,
|
122
|
+
sample_type: @sample_type,
|
123
|
+
channels: @channels,
|
124
|
+
}
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Change the format.
|
129
|
+
#
|
130
|
+
# @note if there is any queued audio it will be cleared,
|
131
|
+
# and the playback will be stopped.
|
132
|
+
#
|
133
|
+
# @param [Hash] format
|
134
|
+
# @option format [Symbol] sample_type only :int16 available
|
135
|
+
# @option format [Integer] sample_rate
|
136
|
+
# @option format [Integer] channels 1 or 2
|
137
|
+
def format=(format)
|
138
|
+
synchronize do
|
139
|
+
if @source.playing?
|
140
|
+
stop
|
141
|
+
@drops += 1
|
142
|
+
end
|
143
|
+
|
144
|
+
@sample_type = format.fetch(:sample_type)
|
145
|
+
@sample_rate = Integer(format.fetch(:sample_rate))
|
146
|
+
@channels = Integer(format.fetch(:channels))
|
147
|
+
|
148
|
+
@sample_format = Formats.fetch([@sample_type, @channels]) do
|
149
|
+
raise TypeError, "unknown sample format for type [#{@sample_type}, #{@channels}]"
|
150
|
+
end
|
151
|
+
|
152
|
+
# 44100 int16s = 22050 frames = 0.5s (1 frame * 2 channels = 2 int16 = 1 sample = 1/44100 s)
|
153
|
+
@buffer_size = @sample_rate * @channels * 1.0
|
154
|
+
# how many samples there are in each buffer, irrespective of channels
|
155
|
+
@buffer_length = @buffer_size / @channels
|
156
|
+
# buffer_duration = buffer_length / sample_rate
|
157
|
+
end
|
94
158
|
end
|
95
159
|
|
96
160
|
# Queue audio frames for playback.
|
97
161
|
#
|
98
|
-
# @
|
162
|
+
# @note this method is here for backwards-compatibility,
|
163
|
+
# and does not support changing format automatically.
|
164
|
+
# You should use {#stream} instead.
|
165
|
+
#
|
166
|
+
# @param [Array<Integer>] array of interleaved audio samples.
|
167
|
+
# @return (see #stream)
|
99
168
|
def <<(frames)
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
169
|
+
stream(frames, format)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Queue audio frames for playback.
|
173
|
+
#
|
174
|
+
# @param [Array<Integer>] array of interleaved audio samples.
|
175
|
+
# @param [Hash] format
|
176
|
+
# @option format [Symbol] :sample_type should be :int16
|
177
|
+
# @option format [Integer] :sample_rate
|
178
|
+
# @option format [Integer] :channels
|
179
|
+
# @return [Integer] number of frames consumed (consumed_samples / channels), a multiple of channels
|
180
|
+
def stream(frames, frame_format)
|
181
|
+
synchronize do
|
182
|
+
if @source.playing? and @source.buffers_processed > 0
|
183
|
+
FFI::MemoryPointer.new(OpenAL::Buffer, @source.buffers_processed) do |ptr|
|
184
|
+
OpenAL.source_unqueue_buffers(@source, ptr.count, ptr)
|
185
|
+
@total_buffers_processed += ptr.count
|
186
|
+
@free_buffers.concat OpenAL::Buffer.extract(ptr, ptr.count)
|
187
|
+
@queued_buffers.delete_if { |buffer| @free_buffers.include?(buffer) }
|
188
|
+
end
|
106
189
|
end
|
107
|
-
end
|
108
190
|
|
109
|
-
|
110
|
-
consumed_frames = frames.take(wanted_size)
|
111
|
-
@queued_frames.concat(consumed_frames)
|
191
|
+
self.format = frame_format if frame_format != format
|
112
192
|
|
113
|
-
|
114
|
-
|
193
|
+
wanted_size = (@buffer_size - @queued_frames.length).div(@channels) * @channels
|
194
|
+
consumed_frames = frames.take(wanted_size)
|
195
|
+
@queued_frames.concat(consumed_frames)
|
115
196
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
197
|
+
if @queued_frames.length >= @buffer_size and @free_buffers.any?
|
198
|
+
current_buffer = @free_buffers.shift
|
199
|
+
|
200
|
+
FFI::MemoryPointer.new(@sample_type, @queued_frames.length) do |frames|
|
201
|
+
frames.public_send(:"write_array_of_#{@sample_type}", @queued_frames)
|
202
|
+
# stereo16 = 2 int16s (1 frame) = 1 sample
|
203
|
+
OpenAL.buffer_data(current_buffer, @sample_format, frames, frames.size, @sample_rate)
|
204
|
+
@queued_frames.clear
|
205
|
+
end
|
206
|
+
|
207
|
+
FFI::MemoryPointer.new(OpenAL::Buffer, 1) do |buffers|
|
208
|
+
buffers.write_uint(current_buffer.to_native)
|
209
|
+
OpenAL.source_queue_buffers(@source, buffers.count, buffers)
|
210
|
+
end
|
122
211
|
|
123
|
-
|
124
|
-
buffers.write_uint(current_buffer.to_native)
|
125
|
-
OpenAL.source_queue_buffers(@source, buffers.count, buffers)
|
212
|
+
@queued_buffers.push(current_buffer)
|
126
213
|
end
|
127
214
|
|
128
|
-
@
|
215
|
+
consumed_frames.length / @channels
|
129
216
|
end
|
130
|
-
|
131
|
-
consumed_frames.length
|
132
217
|
end
|
133
218
|
|
134
219
|
protected
|
135
220
|
|
136
|
-
def
|
137
|
-
@
|
138
|
-
end
|
139
|
-
|
140
|
-
def buffers_processed
|
141
|
-
if not @source.stopped?
|
142
|
-
@source.get(:buffers_processed, Integer)
|
143
|
-
else
|
144
|
-
0
|
145
|
-
end
|
221
|
+
def synchronize
|
222
|
+
@monitor.synchronize { return yield }
|
146
223
|
end
|
147
224
|
end
|
@@ -3,6 +3,37 @@ class Plaything
|
|
3
3
|
class Source < TypeClass(FFI::Type::UINT)
|
4
4
|
include OpenAL::Paramable(:source)
|
5
5
|
|
6
|
+
# Start playback.
|
7
|
+
def play
|
8
|
+
OpenAL.source_play(self)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Pause playback.
|
12
|
+
def pause
|
13
|
+
OpenAL.source_pause(self)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Stop playback and rewind the source.
|
17
|
+
def stop
|
18
|
+
OpenAL.source_stop(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Integer] how many samples (/ channels) that have been played from the queued buffers
|
22
|
+
def sample_offset
|
23
|
+
get(:sample_offset, Integer)
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Integer] number of queued buffers.
|
27
|
+
def buffers_queued
|
28
|
+
get(:buffers_queued, Integer)
|
29
|
+
end
|
30
|
+
|
31
|
+
# @note returns {#buffers_queued} if source is not playing!
|
32
|
+
# @return [Integer] number of processed buffers.
|
33
|
+
def buffers_processed
|
34
|
+
get(:buffers_processed, Integer)
|
35
|
+
end
|
36
|
+
|
6
37
|
# Detach all queued or attached buffers.
|
7
38
|
#
|
8
39
|
# @note all buffers must be processed for this operation to succeed.
|
data/lib/plaything/openal.rb
CHANGED
@@ -5,6 +5,8 @@ class Plaything
|
|
5
5
|
ffi_lib ["openal", "/System/Library/Frameworks/OpenAL.framework/Versions/Current/OpenAL"]
|
6
6
|
|
7
7
|
typedef :pointer, :attributes
|
8
|
+
typedef :pointer, :source_array
|
9
|
+
typedef :pointer, :buffer_array
|
8
10
|
typedef :int, :sizei
|
9
11
|
|
10
12
|
# Errors
|
@@ -61,15 +63,15 @@ class Plaything
|
|
61
63
|
attach_function :alcMakeContextCurrent, [ Context ], :bool
|
62
64
|
|
63
65
|
# Sources
|
64
|
-
attach_function :alGenSources, [ :sizei, :
|
65
|
-
attach_function :alDeleteSources, [ :sizei, :
|
66
|
+
attach_function :alGenSources, [ :sizei, :source_array ], :void
|
67
|
+
attach_function :alDeleteSources, [ :sizei, :source_array ], :void
|
66
68
|
|
67
69
|
attach_function :alSourcePlay, [ Source ], :void
|
68
70
|
attach_function :alSourcePause, [ Source ], :void
|
69
71
|
attach_function :alSourceStop, [ Source ], :void
|
70
72
|
|
71
|
-
attach_function :alSourceQueueBuffers, [ Source, :sizei, :
|
72
|
-
attach_function :alSourceUnqueueBuffers, [ Source, :sizei, :
|
73
|
+
attach_function :alSourceQueueBuffers, [ Source, :sizei, :buffer_array ], :void
|
74
|
+
attach_function :alSourceUnqueueBuffers, [ Source, :sizei, :buffer_array ], :void
|
73
75
|
|
74
76
|
# Buffers
|
75
77
|
enum :format, [
|
@@ -78,8 +80,8 @@ class Plaything
|
|
78
80
|
:stereo8, 0x1102,
|
79
81
|
:stereo16, 0x1103,
|
80
82
|
]
|
81
|
-
attach_function :alGenBuffers, [ :sizei, :
|
82
|
-
attach_function :alDeleteBuffers, [ :sizei, :
|
83
|
+
attach_function :alGenBuffers, [ :sizei, :buffer_array ], :void
|
84
|
+
attach_function :alDeleteBuffers, [ :sizei, :buffer_array ], :void
|
83
85
|
|
84
86
|
attach_function :alBufferData, [ Buffer, :format, :pointer, :sizei, :sizei ], :void
|
85
87
|
|
@@ -13,13 +13,6 @@ class Plaything
|
|
13
13
|
rescue => e
|
14
14
|
warn "release for #{name} failed: #{e.message}."
|
15
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
16
|
end
|
24
17
|
end
|
25
18
|
end
|
@@ -3,17 +3,16 @@ class Plaything
|
|
3
3
|
def self.TypeClass(type)
|
4
4
|
Class.new do
|
5
5
|
extend FFI::DataConverter
|
6
|
-
|
6
|
+
|
7
|
+
define_singleton_method(:type) do
|
8
|
+
type
|
9
|
+
end
|
7
10
|
|
8
11
|
class << self
|
9
12
|
def inherited(other)
|
10
13
|
other.native_type(type)
|
11
14
|
end
|
12
15
|
|
13
|
-
def type
|
14
|
-
@@type
|
15
|
-
end
|
16
|
-
|
17
16
|
def to_native(source, ctx)
|
18
17
|
source.value
|
19
18
|
end
|
@@ -25,12 +24,6 @@ class Plaything
|
|
25
24
|
def size
|
26
25
|
type.size
|
27
26
|
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
27
|
end
|
35
28
|
|
36
29
|
def initialize(value)
|
data/lib/plaything/version.rb
CHANGED
metadata
CHANGED
@@ -1,64 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plaything
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
version: 1.0.0
|
4
|
+
version: 1.1.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Kim Burgestrand
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-04-09 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !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
14
|
name: ffi
|
23
15
|
requirement: !ruby/object:Gem::Requirement
|
24
16
|
requirements:
|
25
17
|
- - ~>
|
26
18
|
- !ruby/object:Gem::Version
|
27
19
|
version: '1.1'
|
28
|
-
none: false
|
29
20
|
type: :runtime
|
30
|
-
|
21
|
+
prerelease: false
|
31
22
|
version_requirements: !ruby/object:Gem::Requirement
|
32
23
|
requirements:
|
33
|
-
- -
|
24
|
+
- - ~>
|
34
25
|
- !ruby/object:Gem::Version
|
35
|
-
version: '
|
36
|
-
|
37
|
-
prerelease: false
|
26
|
+
version: '1.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
38
28
|
name: rspec
|
39
29
|
requirement: !ruby/object:Gem::Requirement
|
40
30
|
requirements:
|
41
|
-
- -
|
31
|
+
- - '>='
|
42
32
|
- !ruby/object:Gem::Version
|
43
33
|
version: '0'
|
44
|
-
none: false
|
45
34
|
type: :development
|
46
|
-
|
35
|
+
prerelease: false
|
47
36
|
version_requirements: !ruby/object:Gem::Requirement
|
48
37
|
requirements:
|
49
|
-
- -
|
38
|
+
- - '>='
|
50
39
|
- !ruby/object:Gem::Version
|
51
40
|
version: '0'
|
52
|
-
|
53
|
-
prerelease: false
|
41
|
+
- !ruby/object:Gem::Dependency
|
54
42
|
name: rake
|
55
43
|
requirement: !ruby/object:Gem::Requirement
|
56
44
|
requirements:
|
57
|
-
- -
|
45
|
+
- - '>='
|
58
46
|
- !ruby/object:Gem::Version
|
59
47
|
version: '0'
|
60
|
-
none: false
|
61
48
|
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
62
55
|
description:
|
63
56
|
email:
|
64
57
|
- kim@burgestrand.se
|
@@ -90,30 +83,26 @@ files:
|
|
90
83
|
homepage: https://github.com/Burgestrand/plaything
|
91
84
|
licenses:
|
92
85
|
- MIT
|
86
|
+
metadata: {}
|
93
87
|
post_install_message:
|
94
88
|
rdoc_options: []
|
95
89
|
require_paths:
|
96
90
|
- lib
|
97
91
|
required_ruby_version: !ruby/object:Gem::Requirement
|
98
92
|
requirements:
|
99
|
-
- -
|
93
|
+
- - '>='
|
100
94
|
- !ruby/object:Gem::Version
|
101
95
|
version: '1.9'
|
102
|
-
none: false
|
103
96
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
97
|
requirements:
|
105
|
-
- -
|
98
|
+
- - '>='
|
106
99
|
- !ruby/object:Gem::Version
|
107
100
|
version: '0'
|
108
|
-
segments:
|
109
|
-
- 0
|
110
|
-
hash: 2964046639458012733
|
111
|
-
none: false
|
112
101
|
requirements: []
|
113
102
|
rubyforge_project:
|
114
|
-
rubygems_version:
|
103
|
+
rubygems_version: 2.0.3
|
115
104
|
signing_key:
|
116
|
-
specification_version:
|
105
|
+
specification_version: 4
|
117
106
|
summary: Blast raw PCM audio through your speakers using OpenAL.
|
118
107
|
test_files:
|
119
108
|
- spec/plaything_spec.rb
|