sound 0.0.4 → 0.0.5
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 +8 -8
- data/examples/example.rb +21 -0
- data/lib/sound.rb +11 -5
- data/lib/sound/data.rb +36 -0
- data/lib/sound/device.rb +172 -0
- data/lib/sound/format.rb +38 -0
- data/lib/sound/linux/sound.rb +25 -0
- data/lib/sound/win32/sound.rb +95 -0
- metadata +9 -6
- data/lib/sound/out.rb +0 -17
- data/lib/sound/sound.rb +0 -4
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZDQzMDA4MjZkZWY3ODQ1YTVmOWJkYzFkOWYxZGUyMWQxMTViZDY5MQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZDU0NDk4ZTBmNjQ5ZmIxOGUyNDI4N2NmOWE5MGMzNWU2ZDQwYTZiOQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YTk5MWFlNmRmZGIwYmM5MzBkNmU2NGYyYjViYjY2NWY1YTc1ZjdiOTU1Y2U3
|
10
|
+
MjdmMmMzMGQ1NDhkZjczMjFhMzM2ZTc2YjNjOWU1OTJiODc1YmQ5ODI5MDJh
|
11
|
+
MzBhOTAxYzU1MWQwNzdiYmNkNGY2YTJmNjM2NGU2Mjg5NjdlNmQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NmZmMzIxNmQ1NDIyOWYyZDk2MzEwNmNiMzAxOWU2MGMwNWE2ZGRkYWJmZDBl
|
14
|
+
OWI4NTM1MDQxNmU0ZDM2OTRiMzQ4MWI0Yzc3NGMwMjQ0MWZhNDg0NTZlOGY3
|
15
|
+
NmI5ZjMzZTJhZTE3Y2EwMGZmZDA3NDkyZGQ3ZTg3ZTYwYjdjN2M=
|
data/examples/example.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'sound'
|
2
|
+
Sound.verbose = true
|
3
|
+
|
4
|
+
# Don't trust these examples. The readme is more up to date.
|
5
|
+
|
6
|
+
# Example of opening a device, writing to it, and closing it up
|
7
|
+
|
8
|
+
device = Sound::Device.new
|
9
|
+
data = Sound::Data.new(device.format)
|
10
|
+
data.generate_sine_wave(440, 500, 1)
|
11
|
+
device.write data
|
12
|
+
device.close
|
13
|
+
|
14
|
+
# This will close the device on its own after the block finishes
|
15
|
+
|
16
|
+
|
17
|
+
#data = WaveData.new
|
18
|
+
#
|
19
|
+
#device = Device.open
|
20
|
+
#device.write(data)
|
21
|
+
#device.close
|
data/lib/sound.rb
CHANGED
@@ -1,10 +1,16 @@
|
|
1
|
-
|
1
|
+
|
2
|
+
require 'ffi'
|
3
|
+
require 'pry'
|
4
|
+
require 'os/os'
|
2
5
|
|
3
6
|
if OS.windows?
|
4
|
-
require 'win32/sound'
|
7
|
+
require 'sound/win32/sound'
|
8
|
+
elsif OS.linux?
|
9
|
+
require 'sound/linux/sound'
|
5
10
|
else
|
6
|
-
warn("Sound output not yet implemented for this platform: #{OS.os}")
|
11
|
+
warn("warning: Sound output not yet implemented for this platform: #{OS.os}")
|
7
12
|
end
|
8
13
|
|
9
|
-
|
10
|
-
|
14
|
+
require 'sound/data'
|
15
|
+
require 'sound/format'
|
16
|
+
require 'sound/device'
|
data/lib/sound/data.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
module Sound
|
3
|
+
|
4
|
+
class Data
|
5
|
+
attr_reader :data, :duration
|
6
|
+
def initialize(format)
|
7
|
+
@format = format
|
8
|
+
@data = []
|
9
|
+
end
|
10
|
+
def generate_sine_wave(freq, duration, volume)
|
11
|
+
@data = []
|
12
|
+
@duration = duration
|
13
|
+
ramp = 200.0
|
14
|
+
samples = (@format.sample_rate/2*duration/1000.0).floor
|
15
|
+
|
16
|
+
samples.times do |sample|
|
17
|
+
|
18
|
+
angle = (2.0*Math::PI*freq) * sample/samples * duration/1000
|
19
|
+
factor = Math.sin(angle)
|
20
|
+
x = 32768.0*factor*volume
|
21
|
+
|
22
|
+
if sample < ramp
|
23
|
+
x *= sample/ramp
|
24
|
+
end
|
25
|
+
if samples - sample < ramp
|
26
|
+
x *= (samples - sample)/ramp
|
27
|
+
end
|
28
|
+
|
29
|
+
@data << x.floor
|
30
|
+
end
|
31
|
+
|
32
|
+
self
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/lib/sound/device.rb
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
|
2
|
+
module Sound
|
3
|
+
|
4
|
+
@verbose = false
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_accessor :verbose
|
8
|
+
end
|
9
|
+
|
10
|
+
WAVE_MAPPER = -1
|
11
|
+
|
12
|
+
class Device
|
13
|
+
|
14
|
+
class Handle
|
15
|
+
def initialize
|
16
|
+
if OS.windows?
|
17
|
+
@handle = Win32::HWAVEOUT.new
|
18
|
+
elsif OS.linux?
|
19
|
+
@handle = FFI::MemoryPointer.new(:pointer)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
def pointer
|
23
|
+
if OS.windows?
|
24
|
+
@handle.pointer
|
25
|
+
elsif OS.linux?
|
26
|
+
@handle
|
27
|
+
end
|
28
|
+
end
|
29
|
+
def id
|
30
|
+
if OS.windows?
|
31
|
+
@handle[:i]
|
32
|
+
elsif OS.linux?
|
33
|
+
@handle.read_pointer
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_accessor :closed, :id, :handle, :format
|
39
|
+
|
40
|
+
def initialize(format = Format::PCM, direction = "w", id = nil)
|
41
|
+
if OS.windows?
|
42
|
+
id ||= WAVE_MAPPER
|
43
|
+
elsif OS.linux?
|
44
|
+
id ||= "default"
|
45
|
+
end
|
46
|
+
@id = id
|
47
|
+
closed = false
|
48
|
+
@queue = []
|
49
|
+
@mutex = Mutex.new
|
50
|
+
@handle = Device::Handle.new
|
51
|
+
@format = format
|
52
|
+
@direction = direction
|
53
|
+
end
|
54
|
+
|
55
|
+
class << self
|
56
|
+
# Opens a sound device for reading or writing
|
57
|
+
# device is one of several devices, usually defined by a constant
|
58
|
+
# direction is reading or writing or both
|
59
|
+
# format is MIDI vs PCM or others
|
60
|
+
# this method can take a block and if so closes the device after execution
|
61
|
+
def open(device = Device::DEFAULT, direction = "w", format = Format::PCM, &block)
|
62
|
+
device.format = format
|
63
|
+
puts "opening device_#{device.id}" if Sound.verbose
|
64
|
+
if block_given?
|
65
|
+
block.call(device)
|
66
|
+
device.close
|
67
|
+
else
|
68
|
+
device.closed = false
|
69
|
+
device
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def play(data = "beep boop")
|
75
|
+
write(data)
|
76
|
+
flush
|
77
|
+
end
|
78
|
+
|
79
|
+
def write(data = "beep boop")
|
80
|
+
if closed?
|
81
|
+
puts "cannot write to a closed device"
|
82
|
+
else
|
83
|
+
@mutex.lock
|
84
|
+
@queue << Thread.new do
|
85
|
+
write_thread(data)
|
86
|
+
end
|
87
|
+
@mutex.unlock
|
88
|
+
puts "writing to device_#{id}_queue: #{data.class}" if Sound.verbose
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def close
|
93
|
+
if closed?
|
94
|
+
puts "cannot close a closed device"
|
95
|
+
else
|
96
|
+
flush
|
97
|
+
puts "device is closing now" if Sound.verbose
|
98
|
+
closed = true
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def flush
|
103
|
+
until @queue.empty?
|
104
|
+
output = @queue.shift
|
105
|
+
output[:stop] = false
|
106
|
+
puts "writing to device_#{id}: #{output}" if Sound.verbose
|
107
|
+
output.run.join
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def closed?
|
112
|
+
closed
|
113
|
+
end
|
114
|
+
|
115
|
+
def write_thread(data)
|
116
|
+
Thread.current[:stop] = true if Thread.current[:stop].nil?
|
117
|
+
if OS.windows?
|
118
|
+
windows_write_thread(data)
|
119
|
+
elsif OS.linux?
|
120
|
+
linux_write_thread(data)
|
121
|
+
else
|
122
|
+
warn("warning: playback is not yet supported on this platform")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def windows_write_thread(data)
|
127
|
+
handle = Handle.new
|
128
|
+
Win32::Sound.waveOutOpen(handle.pointer, id, format.pointer, 0, 0, 0)
|
129
|
+
data_buffer = FFI::MemoryPointer.new(:int, data.data.size)
|
130
|
+
data_buffer.write_array_of_int data.data
|
131
|
+
buffer_length = format.avg_bps*data.duration/1000
|
132
|
+
puts buffer_length
|
133
|
+
header = Win32::WAVEHDR.new(data_buffer, buffer_length)
|
134
|
+
Win32::Sound.waveOutPrepareHeader(handle.id, header.pointer, header.size)
|
135
|
+
Thread.stop if Thread.current[:stop]
|
136
|
+
Win32::Sound.waveOutWrite(handle.id, header.pointer, header.size)
|
137
|
+
while Win32::Sound.waveOutUnprepareHeader(handle.id, header.pointer, header.size) == 33
|
138
|
+
sleep 0.001
|
139
|
+
end
|
140
|
+
Win32::Sound.waveOutClose(handle.id)
|
141
|
+
end
|
142
|
+
|
143
|
+
def linux_write_thread(data)
|
144
|
+
handle = Handle.new
|
145
|
+
AlsaPCM::Sound.snd_pcm_open(handle.pointer, id, 0, 0)
|
146
|
+
data_buffer = FFI::MemoryPointer.new(:int, data.data.size)
|
147
|
+
data_buffer.write_array_of_int data.data
|
148
|
+
buffer_length = data_buffer.size/2
|
149
|
+
params = FFI::MemoryPointer.new(:pointer)
|
150
|
+
AlsaPCM::Sound.snd_pcm_hw_params_malloc(params)
|
151
|
+
AlsaPCM::Sound.snd_pcm_hw_params_any(handle.id, params.read_pointer)
|
152
|
+
|
153
|
+
AlsaPCM::Sound.snd_pcm_hw_params_set_access(handle.id, params.read_pointer, 3)
|
154
|
+
AlsaPCM::Sound.snd_pcm_hw_params_set_format(handle.id, params.read_pointer, 2)
|
155
|
+
AlsaPCM::Sound.snd_pcm_hw_params_set_rate(handle.id, params.read_pointer, 44100, 0)
|
156
|
+
AlsaPCM::Sound.snd_pcm_hw_params_set_channels(handle.id, params.read_pointer, 1)
|
157
|
+
|
158
|
+
AlsaPCM::Sound.snd_pcm_hw_params(handle.id, params.read_pointer)
|
159
|
+
AlsaPCM::Sound.snd_pcm_hw_params_free(params.read_pointer)
|
160
|
+
|
161
|
+
AlsaPCM::Sound.snd_pcm_prepare(handle.id)
|
162
|
+
Thread.stop if Thread.current[:stop]
|
163
|
+
AlsaPCM::Sound.snd_pcm_writei(handle.id, data_buffer, buffer_length)
|
164
|
+
|
165
|
+
|
166
|
+
AlsaPCM::Sound.snd_pcm_drain(handle.id)
|
167
|
+
AlsaPCM::Sound.snd_pcm_close(handle.id)
|
168
|
+
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
data/lib/sound/format.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Sound
|
4
|
+
|
5
|
+
WAVE_FORMAT_PCM = 1
|
6
|
+
|
7
|
+
class Format
|
8
|
+
attr_accessor :channels, :sample_rate, :bps
|
9
|
+
def initialize(format_type = WAVE_FORMAT_PCM)
|
10
|
+
@channels = 1
|
11
|
+
@sample_rate = 44100
|
12
|
+
@bps = 16
|
13
|
+
if OS.windows?
|
14
|
+
@wfx = Win32::WAVEFORMATEX.new
|
15
|
+
@wfx[:wFormatTag] = format_type
|
16
|
+
@wfx[:nChannels] = channels
|
17
|
+
@wfx[:nSamplesPerSec] = sample_rate
|
18
|
+
@wfx[:wBitsPerSample] = bps
|
19
|
+
@wfx[:cbSize] = 0
|
20
|
+
@wfx[:nBlockAlign] = block_align
|
21
|
+
@wfx[:nAvgBytesPerSec] = avg_bps
|
22
|
+
end
|
23
|
+
end
|
24
|
+
def block_align
|
25
|
+
(bps >> 3) * channels
|
26
|
+
end
|
27
|
+
def avg_bps
|
28
|
+
block_align * sample_rate
|
29
|
+
end
|
30
|
+
def pointer
|
31
|
+
if OS.windows?
|
32
|
+
@wfx.pointer
|
33
|
+
end
|
34
|
+
end
|
35
|
+
PCM = self.new
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module AlsaPCM
|
4
|
+
class Sound
|
5
|
+
extend FFI::Library
|
6
|
+
ffi_lib 'asound'
|
7
|
+
ffi_convention :stdcall
|
8
|
+
|
9
|
+
attach_function :snd_pcm_open, [:pointer, :string, :int, :int], :int
|
10
|
+
attach_function :snd_pcm_close, [:pointer], :int
|
11
|
+
attach_function :snd_pcm_drain, [:pointer], :int
|
12
|
+
attach_function :snd_pcm_prepare, [:pointer], :int
|
13
|
+
attach_function :snd_pcm_writen, [:pointer, :pointer, :ulong], :long
|
14
|
+
attach_function :snd_pcm_writei, [:pointer, :pointer, :ulong], :long
|
15
|
+
attach_function :snd_pcm_hw_params_malloc, [:pointer], :int
|
16
|
+
attach_function :snd_pcm_hw_params_any, [:pointer, :pointer], :int
|
17
|
+
attach_function :snd_pcm_hw_params_set_access, [:pointer, :pointer, :int], :int
|
18
|
+
attach_function :snd_pcm_hw_params_set_format, [:pointer, :pointer, :int], :int
|
19
|
+
attach_function :snd_pcm_hw_params_set_rate, [:pointer, :pointer, :uint, :int], :int
|
20
|
+
attach_function :snd_pcm_hw_params_set_channels, [:pointer, :pointer, :int], :int
|
21
|
+
attach_function :snd_pcm_hw_params, [:pointer, :pointer], :int
|
22
|
+
attach_function :snd_pcm_hw_params_free, [:pointer], :void
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
|
2
|
+
module Sound
|
3
|
+
module Win32
|
4
|
+
|
5
|
+
class Sound
|
6
|
+
extend FFI::Library
|
7
|
+
|
8
|
+
typedef :ulong, :dword
|
9
|
+
typedef :uintptr_t, :hwaveout
|
10
|
+
typedef :uint, :mmresult
|
11
|
+
|
12
|
+
ffi_lib :winmm
|
13
|
+
|
14
|
+
attach_function :waveOutOpen, [:pointer, :uint, :pointer, :dword, :dword, :dword], :mmresult
|
15
|
+
attach_function :waveOutPrepareHeader, [:hwaveout, :pointer, :uint], :mmresult
|
16
|
+
attach_function :waveOutWrite, [:hwaveout, :pointer, :uint], :mmresult
|
17
|
+
attach_function :waveOutUnprepareHeader, [:hwaveout, :pointer, :uint], :mmresult
|
18
|
+
attach_function :waveOutClose, [:hwaveout], :mmresult
|
19
|
+
end
|
20
|
+
|
21
|
+
WAVE_FORMAT_PCM = 1
|
22
|
+
|
23
|
+
# Define an HWAVEOUT struct for use by all the waveOut functions.
|
24
|
+
# It is a handle to a waveOut stream, so starting up multiple
|
25
|
+
# streams using different handles allows for simultaneous playback.
|
26
|
+
# You never need to actually look at the struct, C takes care of
|
27
|
+
# its value.
|
28
|
+
#
|
29
|
+
class HWAVEOUT < FFI::Struct
|
30
|
+
layout :i, :int
|
31
|
+
end
|
32
|
+
|
33
|
+
# Define WAVEFORMATEX which defines the format (PCM in this case)
|
34
|
+
# and various properties like sampling rate, number of channels, etc.
|
35
|
+
#
|
36
|
+
class WAVEFORMATEX < FFI::Struct
|
37
|
+
|
38
|
+
# Initializes struct with sensible defaults for most commonly used
|
39
|
+
# values. While setting these manually is possible, please be
|
40
|
+
# sure you know what changes will result in, as an incorrectly
|
41
|
+
# set struct will result in unpredictable behavior.
|
42
|
+
#
|
43
|
+
def initialize(nSamplesPerSec = 44100, wBitsPerSample = 16, nChannels = 1, cbSize = 0)
|
44
|
+
self[:wFormatTag] = WAVE_FORMAT_PCM
|
45
|
+
self[:nChannels] = nChannels
|
46
|
+
self[:nSamplesPerSec] = nSamplesPerSec
|
47
|
+
self[:wBitsPerSample] = wBitsPerSample
|
48
|
+
self[:cbSize] = cbSize
|
49
|
+
self[:nBlockAlign] = (self[:wBitsPerSample] >> 3) * self[:nChannels]
|
50
|
+
self[:nAvgBytesPerSec] = self[:nBlockAlign] * self[:nSamplesPerSec]
|
51
|
+
end
|
52
|
+
|
53
|
+
layout(
|
54
|
+
:wFormatTag, :ushort,
|
55
|
+
:nChannels, :ushort,
|
56
|
+
:nSamplesPerSec, :ulong,
|
57
|
+
:nAvgBytesPerSec, :ulong,
|
58
|
+
:nBlockAlign, :ushort,
|
59
|
+
:wBitsPerSample, :ushort,
|
60
|
+
:cbSize, :ushort
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
#define WAVEHDR which is a header to a block of audio
|
65
|
+
#lpData is a pointer to the block of native memory that,
|
66
|
+
# in this case, is an integer array of PCM data
|
67
|
+
|
68
|
+
class WAVEHDR < FFI::Struct
|
69
|
+
|
70
|
+
# Initializes struct with sensible defaults for most commonly used
|
71
|
+
# values. While setting these manually is possible, please be
|
72
|
+
# sure you know what changes will result in, as an incorrectly
|
73
|
+
# set struct will result in unpredictable behavior.
|
74
|
+
#
|
75
|
+
def initialize(lpData, dwBufferLength, dwFlags = 0, dwLoops = 1)
|
76
|
+
self[:lpData] = lpData
|
77
|
+
self[:dwBufferLength] = dwBufferLength
|
78
|
+
self[:dwFlags] = dwFlags
|
79
|
+
self[:dwLoops] = dwLoops
|
80
|
+
end
|
81
|
+
|
82
|
+
layout(
|
83
|
+
:lpData, :pointer,
|
84
|
+
:dwBufferLength, :ulong,
|
85
|
+
:dwBytesRecorded, :ulong,
|
86
|
+
:dwUser, :ulong,
|
87
|
+
:dwFlags, :ulong,
|
88
|
+
:dwLoops, :ulong,
|
89
|
+
:lpNext, :pointer,
|
90
|
+
:reserved, :ulong
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sound
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dominic Muller
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Allows for effiecent cross-platform sound libraries in pure Ruby by tapping
|
14
14
|
into native libraries.
|
@@ -17,10 +17,14 @@ executables: []
|
|
17
17
|
extensions: []
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
|
+
- examples/example.rb
|
20
21
|
- lib/os/os.rb
|
21
22
|
- lib/sound.rb
|
22
|
-
- lib/sound/
|
23
|
-
- lib/sound/
|
23
|
+
- lib/sound/data.rb
|
24
|
+
- lib/sound/device.rb
|
25
|
+
- lib/sound/format.rb
|
26
|
+
- lib/sound/linux/sound.rb
|
27
|
+
- lib/sound/win32/sound.rb
|
24
28
|
homepage: https://github.com/RSMP/sound
|
25
29
|
licenses:
|
26
30
|
- MIT
|
@@ -39,8 +43,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
39
43
|
- - ! '>='
|
40
44
|
- !ruby/object:Gem::Version
|
41
45
|
version: '0'
|
42
|
-
requirements:
|
43
|
-
- win32-sound, '>= 0.6.0' on Windows
|
46
|
+
requirements: []
|
44
47
|
rubyforge_project:
|
45
48
|
rubygems_version: 2.2.2
|
46
49
|
signing_key:
|
data/lib/sound/out.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
|
2
|
-
module Sound
|
3
|
-
|
4
|
-
module Out
|
5
|
-
|
6
|
-
def self.play_freq(frequency = 440, duration = 1000, volume = 1, immediate_playback = true)
|
7
|
-
if OS.windows?
|
8
|
-
Win32::Sound.play_freq(frequency, duration, volume, immediate_playback)
|
9
|
-
else
|
10
|
-
warn("play_freq not implemented for this platform: #{RUBY_PLATFORM}")
|
11
|
-
end
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
data/lib/sound/sound.rb
DELETED