sound 0.0.6 → 0.0.7
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 +142 -9
- data/lib/sound.rb +34 -3
- data/lib/sound/alsa.rb +211 -0
- data/lib/sound/data.rb +9 -5
- data/lib/sound/device.rb +121 -108
- data/lib/sound/format.rb +4 -3
- data/lib/sound/{win32/sound.rb → win32.rb} +68 -13
- metadata +33 -5
- data/lib/sound/linux/sound.rb +0 -25
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MjJmMzc0MDMzOTQ5ZjdjNDk4NzI0ZGMyYmM3NWE0NjdhZjA4YTI0Yw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YjIxMDAwMTgwMDMzNTdhOTZiNzQzNTk1MTNkMDEwYjAxMGZkNmQ5OQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MjJlYjk0NDc5NTdiOWNlYjhjMGE2NTIxODNjZDE3YjU2OWYxMGUyOTZlMTU5
|
10
|
+
MTU1Y2UzNjRhNTA1MGZhNzgxYzlmOTAwZDBjMjBjMzhmMThhYmMzOTkxYzQy
|
11
|
+
OGNhN2U5ZTRkZTYwNGE2ZmE4OThjYmU4MWJlNzgyMDQzZDEwY2M=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZTJjZjJmN2EyMzcyN2RkNDdiN2VlNDNhZDMxM2E2OGRkNTgwMzQ3NDE2ZTQ0
|
14
|
+
NzE3MWYwNzYyMGM1NzA3YWJmOWNkY2MxYjU1ODY3NGI2NzhkNzQyODRiYzMz
|
15
|
+
NTRmMmY4MDM3YzE0MGYwYjE0NzI4YzE1YWQzZGI2OWQ0YmI1Nzg=
|
data/examples/example.rb
CHANGED
@@ -1,21 +1,154 @@
|
|
1
1
|
require 'sound'
|
2
|
-
Sound.verbose = true
|
3
2
|
|
3
|
+
# this will alert you when various events occur
|
4
|
+
# such as when a device is opened or closed, when
|
5
|
+
# data is added to the queue, etc.
|
6
|
+
Sound.verbose = true
|
4
7
|
# Don't trust these examples. The readme is more up to date.
|
5
8
|
|
6
9
|
# Example of opening a device, writing to it, and closing it up
|
7
10
|
|
11
|
+
# opens a device for writing
|
12
|
+
device = Sound::Device.new("w")
|
13
|
+
|
14
|
+
# creates a PCM format object
|
15
|
+
format = Sound::Format.new(Sound::Format::PCM)
|
16
|
+
|
17
|
+
# creates a new Sound Data object with a PCM format
|
18
|
+
data = Sound::Data.new(format)
|
19
|
+
|
20
|
+
# writes a sine wave to the data object with
|
21
|
+
# a frequency of 440 Hz, a duration of 500 ms
|
22
|
+
# and a volume of 1 (full volume)
|
23
|
+
data.generate_sine_wave(880, 500, 1)
|
24
|
+
|
25
|
+
# writes data to the message queue
|
26
|
+
device.write data
|
27
|
+
|
28
|
+
# flushes the message queue
|
29
|
+
device.flush
|
30
|
+
|
31
|
+
# responsibly closes the device
|
32
|
+
device.close
|
33
|
+
|
34
|
+
# with sensible defaults, you can ignore some of this
|
35
|
+
|
8
36
|
device = Sound::Device.new
|
9
|
-
data = Sound::Data.new
|
37
|
+
data = Sound::Data.new
|
10
38
|
data.generate_sine_wave(440, 500, 1)
|
11
|
-
device.
|
39
|
+
device.play data
|
12
40
|
device.close
|
13
41
|
|
14
|
-
#
|
42
|
+
# note the lack of specifying a format or explicitly writing the data
|
43
|
+
# to the message queue. the #play method writes the data to the queue
|
44
|
+
# and flushes it automatically.
|
45
|
+
|
46
|
+
# with the block form, which uses #open instead of #new, it's even more
|
47
|
+
# cogent, since it will close the device for you
|
48
|
+
|
49
|
+
Sound::Device.new {|d| d.play Sound::Data.new.sine_wave(440, 500, 1)}
|
50
|
+
|
51
|
+
# Let's play a little melody! It starts to get out of sync about halfway through
|
52
|
+
|
53
|
+
threads = []
|
54
|
+
|
55
|
+
threads << Thread.new do
|
56
|
+
Sound::Device.new do |device|
|
57
|
+
device.write Sound::Data.new.sine_wave(440, 1200, 0.4)
|
58
|
+
device.write Sound::Data.new.sine_wave(440*2**(4/12.0), 600, 0.4)
|
59
|
+
device.write Sound::Data.new.sine_wave(440*2**(7/12.0), 600, 0.4)
|
60
|
+
device.write Sound::Data.new.sine_wave(440*2**(-1/12.0), 900, 0.4)
|
61
|
+
device.write Sound::Data.new.sine_wave(440*2**(0/12.0), 150, 0.4)
|
62
|
+
device.write Sound::Data.new.sine_wave(440*2**(2/12.0), 150, 0.4)
|
63
|
+
device.write Sound::Data.new.sine_wave(440*2**(0/12.0), 1200, 0.4)
|
64
|
+
device.write Sound::Data.new.sine_wave(440*2**(9/12.0), 1200, 0.4)
|
65
|
+
device.write Sound::Data.new.sine_wave(440*2**(7/12.0), 600, 0.4)
|
66
|
+
device.write Sound::Data.new.sine_wave(440*2**(12/12.0), 600, 0.4)
|
67
|
+
device.write Sound::Data.new.sine_wave(440*2**(7/12.0), 600, 0.4)
|
68
|
+
device.write Sound::Data.new.sine_wave(440*2**(5/12.0), 70, 0.4)
|
69
|
+
device.write Sound::Data.new.sine_wave(440*2**(7/12.0), 70, 0.4)
|
70
|
+
device.write Sound::Data.new.sine_wave(440*2**(5/12.0), 70, 0.4)
|
71
|
+
device.write Sound::Data.new.sine_wave(440*2**(7/12.0), 70, 0.4)
|
72
|
+
device.write Sound::Data.new.sine_wave(440*2**(5/12.0), 170, 0.4)
|
73
|
+
device.write Sound::Data.new.sine_wave(440*2**(4/12.0), 150, 0.4)
|
74
|
+
device.write Sound::Data.new.sine_wave(440*2**(4/12.0), 1200, 0.4)
|
75
|
+
Thread.pass
|
76
|
+
Thread.pass
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
threads << Thread.new do
|
81
|
+
Sound::Device.new do |device|
|
82
|
+
device.write Sound::Data.new.sine_wave(220*2**(0/12.0), 300, 0.4)
|
83
|
+
device.write Sound::Data.new.sine_wave(220*2**(7/12.0), 300, 0.4)
|
84
|
+
device.write Sound::Data.new.sine_wave(220*2**(4/12.0), 300, 0.4)
|
85
|
+
device.write Sound::Data.new.sine_wave(220*2**(7/12.0), 300, 0.4)
|
86
|
+
|
87
|
+
device.write Sound::Data.new.sine_wave(220*2**(0/12.0), 300, 0.4)
|
88
|
+
device.write Sound::Data.new.sine_wave(220*2**(7/12.0), 300, 0.4)
|
89
|
+
device.write Sound::Data.new.sine_wave(220*2**(4/12.0), 300, 0.4)
|
90
|
+
device.write Sound::Data.new.sine_wave(220*2**(7/12.0), 300, 0.4)
|
91
|
+
|
92
|
+
device.write Sound::Data.new.sine_wave(220*2**(-1/12.0), 300, 0.4)
|
93
|
+
device.write Sound::Data.new.sine_wave(220*2**(7/12.0), 300, 0.4)
|
94
|
+
device.write Sound::Data.new.sine_wave(220*2**(5/12.0), 300, 0.4)
|
95
|
+
device.write Sound::Data.new.sine_wave(220*2**(7/12.0), 300, 0.4)
|
96
|
+
|
97
|
+
device.write Sound::Data.new.sine_wave(220*2**(0/12.0), 300, 0.4)
|
98
|
+
device.write Sound::Data.new.sine_wave(220*2**(7/12.0), 300, 0.4)
|
99
|
+
device.write Sound::Data.new.sine_wave(220*2**(4/12.0), 300, 0.4)
|
100
|
+
device.write Sound::Data.new.sine_wave(220*2**(7/12.0), 300, 0.4)
|
101
|
+
|
102
|
+
device.write Sound::Data.new.sine_wave(220*2**(0/12.0), 300, 0.4)
|
103
|
+
device.write Sound::Data.new.sine_wave(220*2**(9/12.0), 300, 0.4)
|
104
|
+
device.write Sound::Data.new.sine_wave(220*2**(5/12.0), 300, 0.4)
|
105
|
+
device.write Sound::Data.new.sine_wave(220*2**(9/12.0), 300, 0.4)
|
106
|
+
|
107
|
+
device.write Sound::Data.new.sine_wave(220*2**(0/12.0), 300, 0.4)
|
108
|
+
device.write Sound::Data.new.sine_wave(220*2**(7/12.0), 300, 0.4)
|
109
|
+
device.write Sound::Data.new.sine_wave(220*2**(4/12.0), 300, 0.4)
|
110
|
+
device.write Sound::Data.new.sine_wave(220*2**(7/12.0), 300, 0.4)
|
111
|
+
|
112
|
+
device.write Sound::Data.new.sine_wave(220*2**(-1/12.0), 300, 0.4)
|
113
|
+
device.write Sound::Data.new.sine_wave(220*2**(7/12.0), 300, 0.4)
|
114
|
+
device.write Sound::Data.new.sine_wave(220*2**(5/12.0), 300, 0.4)
|
115
|
+
device.write Sound::Data.new.sine_wave(220*2**(7/12.0), 300, 0.4)
|
116
|
+
|
117
|
+
device.write Sound::Data.new.sine_wave(220*2**(0/12.0), 300, 0.4)
|
118
|
+
device.write Sound::Data.new.sine_wave(220*2**(7/12.0), 300, 0.4)
|
119
|
+
device.write Sound::Data.new.sine_wave(220*2**(4/12.0), 300, 0.4)
|
120
|
+
device.write Sound::Data.new.sine_wave(220*2**(7/12.0), 300, 0.4)
|
121
|
+
Thread.pass
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
threads.each {|t| t.join}
|
126
|
+
#example of asynchronous playback
|
127
|
+
|
128
|
+
Sound::Device.new do |device|
|
129
|
+
device.write_async Sound::Data.new.sine_wave(440, 300, 1)
|
130
|
+
device.write_async Sound::Data.new.sine_wave(660, 300, 1)
|
131
|
+
device.write Sound::Data.new.sine_wave(660, 300, 1)
|
132
|
+
device.write_async Sound::Data.new.sine_wave(440, 300, 1), true
|
133
|
+
device.write_async Sound::Data.new.sine_wave(880, 300, 1)
|
134
|
+
device.write_async Sound::Data.new.sine_wave(440, 300, 1), true
|
135
|
+
device.write_async Sound::Data.new.sine_wave(880*2**(4/12.0), 300, 1)
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
threads = []
|
15
140
|
|
141
|
+
threads << Thread.new do
|
142
|
+
Sound::Device.new do |device|
|
143
|
+
device.write Sound::Data.new.sine_wave(440, 500, 1)
|
144
|
+
Thread.pass
|
145
|
+
end
|
146
|
+
end
|
147
|
+
threads << Thread.new do
|
148
|
+
Sound::Device.new do |device|
|
149
|
+
device.write Sound::Data.new.sine_wave(660, 500, 1)
|
150
|
+
Thread.pass
|
151
|
+
end
|
152
|
+
end
|
16
153
|
|
17
|
-
|
18
|
-
#
|
19
|
-
#device = Device.open
|
20
|
-
#device.write(data)
|
21
|
-
#device.close
|
154
|
+
threads.each {|t| t.join}
|
data/lib/sound.rb
CHANGED
@@ -3,14 +3,45 @@ require 'ffi'
|
|
3
3
|
require 'pry'
|
4
4
|
require 'os/os'
|
5
5
|
|
6
|
+
module Sound
|
7
|
+
|
8
|
+
@verbose = false
|
9
|
+
@no_device = false
|
10
|
+
@platform_supported = false
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_accessor :verbose, :no_device, :platform_supported
|
14
|
+
end
|
15
|
+
|
16
|
+
class NoDeviceError < RuntimeError; end
|
17
|
+
class NoDependencyError < RuntimeError; end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
6
21
|
if OS.windows?
|
7
|
-
require 'sound/win32
|
22
|
+
require 'sound/win32'
|
23
|
+
module Sound
|
24
|
+
class Device
|
25
|
+
include Win32
|
26
|
+
end
|
27
|
+
end
|
28
|
+
Sound.platform_supported = true
|
8
29
|
elsif OS.linux?
|
9
|
-
|
30
|
+
libasound_present = !(`which aplay`.eql? "")
|
31
|
+
unless libasound_present
|
32
|
+
warn("warning: sound output requires libasound2, libasound2-dev, and alsa-utils packages")
|
33
|
+
end
|
34
|
+
require 'sound/alsa'
|
35
|
+
module Sound
|
36
|
+
class Device
|
37
|
+
include ALSA
|
38
|
+
end
|
39
|
+
end
|
40
|
+
Sound.platform_supported = true
|
10
41
|
else
|
11
42
|
warn("warning: Sound output not yet implemented for this platform: #{OS.os}")
|
12
43
|
end
|
13
44
|
|
45
|
+
require 'sound/device'
|
14
46
|
require 'sound/data'
|
15
47
|
require 'sound/format'
|
16
|
-
require 'sound/device'
|
data/lib/sound/alsa.rb
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module Sound
|
4
|
+
module ALSA
|
5
|
+
|
6
|
+
class Handle
|
7
|
+
def initialize
|
8
|
+
@handle = FFI::MemoryPointer.new(:pointer)
|
9
|
+
end
|
10
|
+
def pointer
|
11
|
+
@handle
|
12
|
+
end
|
13
|
+
def id
|
14
|
+
@handle.read_pointer
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
SND_PCM_STREAM_PLAYBACK = 0
|
19
|
+
SND_PCM_STREAM_CAPTURE = 1
|
20
|
+
DEFAULT_DEVICE_ID = "default"
|
21
|
+
|
22
|
+
extend FFI::Library
|
23
|
+
ffi_lib 'asound'
|
24
|
+
ffi_convention :stdcall
|
25
|
+
|
26
|
+
attach_function :snd_pcm_open, [:pointer, :string, :int, :int], :int
|
27
|
+
attach_function :snd_pcm_close, [:pointer], :int
|
28
|
+
attach_function :snd_pcm_drain, [:pointer], :int
|
29
|
+
attach_function :snd_pcm_prepare, [:pointer], :int
|
30
|
+
attach_function :write_noninterleaved, :snd_pcm_writen, [:pointer, :pointer, :ulong], :long
|
31
|
+
attach_function :snd_pcm_writei, [:pointer, :pointer, :ulong], :long
|
32
|
+
attach_function :snd_pcm_hw_params_malloc, [:pointer], :int
|
33
|
+
attach_function :snd_pcm_hw_params_any, [:pointer, :pointer], :int
|
34
|
+
attach_function :snd_pcm_hw_params_set_access, [:pointer, :pointer, :int], :int
|
35
|
+
attach_function :snd_pcm_hw_params_set_format, [:pointer, :pointer, :int], :int
|
36
|
+
attach_function :snd_pcm_hw_params_set_rate, [:pointer, :pointer, :uint, :int], :int
|
37
|
+
attach_function :snd_pcm_hw_params_set_channels, [:pointer, :pointer, :int], :int
|
38
|
+
attach_function :snd_pcm_hw_params, [:pointer, :pointer], :int
|
39
|
+
attach_function :snd_pcm_hw_params_free, [:pointer], :void
|
40
|
+
|
41
|
+
def snd_pcm_open(*args)
|
42
|
+
output = `aplay -l 2>&1`
|
43
|
+
if output.match(/no soundcard/m)
|
44
|
+
raise NoDeviceError, "No sound devices present"
|
45
|
+
elsif output.match(/not found/m)
|
46
|
+
raise NoDependencyError, "aplay is not present in your environment. Install alsa-utils package for audio playback."
|
47
|
+
else
|
48
|
+
snd_pcm_open(*args)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def open_device
|
53
|
+
begin
|
54
|
+
snd_pcm_open(handle.pointer, id, 0, ASYNC)
|
55
|
+
rescue NoDeviceError
|
56
|
+
Sound.no_device = true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def prepare_buffer
|
61
|
+
|
62
|
+
unless Sound.no_device
|
63
|
+
buffer_length
|
64
|
+
|
65
|
+
snd_pcm_hw_params_malloc(params_handle.pointer)
|
66
|
+
snd_pcm_hw_params_any(handle.id, params_handle.id)
|
67
|
+
|
68
|
+
snd_pcm_hw_params_set_access(handle.id, params_handle.id, SND_PCM_ACCESS_RW_INTERLEAVED)
|
69
|
+
set_formsnd_pcm_hw_params_set_formatat(handle.id, params_handle.id, SND_PCM_FORMAT_S16_LE)
|
70
|
+
# need to change this to set_rate_near at some point
|
71
|
+
snd_pcm_hw_params_set_rate(handle.id, params_handle.id, data.format.sample_rate, 0)
|
72
|
+
snd_pcm_hw_params_set_channels(handle.id, params_handle.id, 1)
|
73
|
+
|
74
|
+
snd_pcm_hw_params(handle.id, params_handle.id)
|
75
|
+
snd_pcm_hw_params_free(params_handle.id)
|
76
|
+
|
77
|
+
snd_pcm_prepare(handle.id)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
def write_to_device
|
83
|
+
snd_pcm_writei(handle.id, data_buffer, buffer_length) unless Sound.no_device
|
84
|
+
end
|
85
|
+
|
86
|
+
def unprepare_buffer
|
87
|
+
snd_pcm_drain(handle.id) unless Sound.no_device
|
88
|
+
end
|
89
|
+
|
90
|
+
def close_device
|
91
|
+
snd_pcm_close(handle.id) unless Sound.no_device
|
92
|
+
end
|
93
|
+
|
94
|
+
def handle
|
95
|
+
Thread.current[:handle] ||= Handle.new
|
96
|
+
end
|
97
|
+
|
98
|
+
def params_handle
|
99
|
+
Thread.current[:params_handle] ||= Handle.new
|
100
|
+
end
|
101
|
+
|
102
|
+
def data
|
103
|
+
Thread.current[:data]
|
104
|
+
end
|
105
|
+
|
106
|
+
def data_buffer
|
107
|
+
Thread.current[:data_buffer] ||= FFI::MemoryPointer.new(:int, data.pcm_data.size).write_array_of_int data.pcm_data
|
108
|
+
end
|
109
|
+
|
110
|
+
def buffer_length
|
111
|
+
Thread.current[:buffer_length] ||= data_buffer.size/2
|
112
|
+
end
|
113
|
+
|
114
|
+
SND_PCM_ASYNC = 2
|
115
|
+
ASYNC = SND_PCM_ASYNC
|
116
|
+
#snd_pcm formats
|
117
|
+
# Unknown
|
118
|
+
SND_PCM_FORMAT_UNKNOWN = -1
|
119
|
+
# Signed 8 bit
|
120
|
+
SND_PCM_FORMAT_S8 = 0
|
121
|
+
# Unsigned 8 bit
|
122
|
+
SND_PCM_FORMAT_U8 = 1
|
123
|
+
# Signed 16 bit Little Endian
|
124
|
+
SND_PCM_FORMAT_S16_LE = 2
|
125
|
+
# Signed 16 bit Big Endian
|
126
|
+
SND_PCM_FORMAT_S16_BE = 3
|
127
|
+
# Unsigned 16 bit Little Endian
|
128
|
+
SND_PCM_FORMAT_U16_LE = 4
|
129
|
+
# Unsigned 16 bit Big Endian
|
130
|
+
SND_PCM_FORMAT_U16_BE = 5
|
131
|
+
# Signed 24 bit Little Endian using low three bytes in 32-bit word
|
132
|
+
SND_PCM_FORMAT_S24_LE = 6
|
133
|
+
# Signed 24 bit Big Endian using low three bytes in 32-bit word
|
134
|
+
SND_PCM_FORMAT_S24_BE = 7
|
135
|
+
# Unsigned 24 bit Little Endian using low three bytes in 32-bit word
|
136
|
+
SND_PCM_FORMAT_U24_LE = 8
|
137
|
+
# Unsigned 24 bit Big Endian using low three bytes in 32-bit word
|
138
|
+
SND_PCM_FORMAT_U24_BE = 9
|
139
|
+
# Signed 32 bit Little Endian
|
140
|
+
SND_PCM_FORMAT_S32_LE = 10
|
141
|
+
# Signed 32 bit Big Endian
|
142
|
+
SND_PCM_FORMAT_S32_BE = 11
|
143
|
+
# Unsigned 32 bit Little Endian
|
144
|
+
SND_PCM_FORMAT_U32_LE = 12
|
145
|
+
# Unsigned 32 bit Big Endian
|
146
|
+
SND_PCM_FORMAT_U32_BE = 13
|
147
|
+
# Float 32 bit Little Endian, Range -1.0 to 1.0
|
148
|
+
SND_PCM_FORMAT_FLOAT_LE = 14
|
149
|
+
# Float 32 bit Big Endian, Range -1.0 to 1.0
|
150
|
+
SND_PCM_FORMAT_FLOAT_BE = 15
|
151
|
+
# Float 64 bit Little Endian, Range -1.0 to 1.0
|
152
|
+
SND_PCM_FORMAT_FLOAT64_LE = 16
|
153
|
+
# Float 64 bit Big Endian, Range -1.0 to 1.0
|
154
|
+
SND_PCM_FORMAT_FLOAT64_BE = 17
|
155
|
+
# IEC-958 Little Endian
|
156
|
+
SND_PCM_FORMAT_IEC958_SUBFRAME_LE = 18
|
157
|
+
# IEC-958 Big Endian
|
158
|
+
SND_PCM_FORMAT_IEC958_SUBFRAME_BE = 19
|
159
|
+
# Mu-Law
|
160
|
+
SND_PCM_FORMAT_MU_LAW = 20
|
161
|
+
# A-Law
|
162
|
+
SND_PCM_FORMAT_A_LAW = 21
|
163
|
+
# Ima-ADPCM
|
164
|
+
SND_PCM_FORMAT_IMA_ADPCM = 22
|
165
|
+
# MPEG
|
166
|
+
SND_PCM_FORMAT_MPEG = 23
|
167
|
+
# GSM
|
168
|
+
SND_PCM_FORMAT_GSM = 24
|
169
|
+
# Special
|
170
|
+
SND_PCM_FORMAT_SPECIAL = 31
|
171
|
+
# Signed 24bit Little Endian in 3bytes format
|
172
|
+
SND_PCM_FORMAT_S24_3LE = 32
|
173
|
+
# Signed 24bit Big Endian in 3bytes format
|
174
|
+
SND_PCM_FORMAT_S24_3BE = 33
|
175
|
+
# Unsigned 24bit Little Endian in 3bytes format
|
176
|
+
SND_PCM_FORMAT_U24_3LE = 34
|
177
|
+
# Unsigned 24bit Big Endian in 3bytes format
|
178
|
+
SND_PCM_FORMAT_U24_3BE = 35
|
179
|
+
# Signed 20bit Little Endian in 3bytes format
|
180
|
+
SND_PCM_FORMAT_S20_3LE = 36
|
181
|
+
# Signed 20bit Big Endian in 3bytes format
|
182
|
+
SND_PCM_FORMAT_S20_3BE = 37
|
183
|
+
# Unsigned 20bit Little Endian in 3bytes format
|
184
|
+
SND_PCM_FORMAT_U20_3LE = 38
|
185
|
+
# Unsigned 20bit Big Endian in 3bytes format
|
186
|
+
SND_PCM_FORMAT_U20_3BE = 39
|
187
|
+
# Signed 18bit Little Endian in 3bytes format
|
188
|
+
SND_PCM_FORMAT_S18_3LE = 40
|
189
|
+
# Signed 18bit Big Endian in 3bytes format
|
190
|
+
SND_PCM_FORMAT_S18_3BE = 41
|
191
|
+
# Unsigned 18bit Little Endian in 3bytes format
|
192
|
+
SND_PCM_FORMAT_U18_3LE = 42
|
193
|
+
# Unsigned 18bit Big Endian in 3bytes format
|
194
|
+
SND_PCM_FORMAT_U18_3BE = 43
|
195
|
+
SND_PCM_FORMAT_LAST = SND_PCM_FORMAT_U18_3BE
|
196
|
+
|
197
|
+
#snd_pcm access
|
198
|
+
# mmap access with simple interleaved channels
|
199
|
+
SND_PCM_ACCESS_MMAP_INTERLEAVED = 0
|
200
|
+
# mmap access with simple non interleaved channels
|
201
|
+
SND_PCM_ACCESS_MMAP_NONINTERLEAVED = 1
|
202
|
+
# mmap access with complex placement
|
203
|
+
SND_PCM_ACCESS_MMAP_COMPLEX = 2
|
204
|
+
# snd_pcm_readi/snd_pcm_writei access
|
205
|
+
SND_PCM_ACCESS_RW_INTERLEAVED = 3
|
206
|
+
# snd_pcm_readn/snd_pcm_writen access
|
207
|
+
SND_PCM_ACCESS_RW_NONINTERLEAVED = 4
|
208
|
+
SND_PCM_ACCESS_LAST = SND_PCM_ACCESS_RW_NONINTERLEAVED
|
209
|
+
|
210
|
+
end
|
211
|
+
end
|
data/lib/sound/data.rb
CHANGED
@@ -2,13 +2,16 @@
|
|
2
2
|
module Sound
|
3
3
|
|
4
4
|
class Data
|
5
|
-
attr_reader :
|
6
|
-
def initialize(format)
|
5
|
+
attr_reader :pcm_data, :duration, :format
|
6
|
+
def initialize(format = Format.new)
|
7
7
|
@format = format
|
8
|
-
@
|
8
|
+
@pcm_data = []
|
9
|
+
end
|
10
|
+
def to_s
|
11
|
+
puts "#<Sound::Data:#{object_id}>"
|
9
12
|
end
|
10
13
|
def generate_sine_wave(freq, duration, volume)
|
11
|
-
@
|
14
|
+
@pcm_data = []
|
12
15
|
@duration = duration
|
13
16
|
ramp = 200.0
|
14
17
|
samples = (@format.sample_rate/2*duration/1000.0).floor
|
@@ -26,11 +29,12 @@ module Sound
|
|
26
29
|
x *= (samples - sample)/ramp
|
27
30
|
end
|
28
31
|
|
29
|
-
@
|
32
|
+
@pcm_data << x.floor
|
30
33
|
end
|
31
34
|
|
32
35
|
self
|
33
36
|
end
|
37
|
+
alias :sine_wave :generate_sine_wave
|
34
38
|
end
|
35
39
|
|
36
40
|
end
|
data/lib/sound/device.rb
CHANGED
@@ -1,55 +1,50 @@
|
|
1
1
|
|
2
2
|
module Sound
|
3
|
-
|
4
|
-
@verbose = false
|
5
|
-
|
6
|
-
class << self
|
7
|
-
attr_accessor :verbose
|
8
|
-
end
|
9
|
-
|
10
|
-
WAVE_MAPPER = -1
|
11
3
|
|
12
4
|
class Device
|
13
5
|
|
14
|
-
class
|
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
|
6
|
+
class Buffer < Array; end
|
37
7
|
|
38
|
-
|
8
|
+
attr_reader :status, :id
|
39
9
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
10
|
+
# creates a new device for writing by default. default id is set by
|
11
|
+
# whatever device interface was included (Win32 or ALSA, e.g.)
|
12
|
+
# if a block is passed, it executes the code in the block, passing
|
13
|
+
# the newly created device, and then closes the device.
|
14
|
+
#
|
15
|
+
def initialize(direction = "w", id = DEFAULT_DEVICE_ID, &block)
|
16
|
+
|
46
17
|
@id = id
|
47
|
-
|
48
|
-
@queue =
|
18
|
+
@status = :open
|
19
|
+
@queue = Device::Buffer.new
|
49
20
|
@mutex = Mutex.new
|
50
|
-
@handle = Device::Handle.new
|
51
|
-
@format = format
|
52
21
|
@direction = direction
|
22
|
+
|
23
|
+
puts "opening device: '#{id}'" if Sound.verbose
|
24
|
+
|
25
|
+
if block_given?
|
26
|
+
block.call(self)
|
27
|
+
close
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
# checks if the current status of the device is :open
|
33
|
+
#
|
34
|
+
def open?
|
35
|
+
status == :open
|
36
|
+
end
|
37
|
+
|
38
|
+
# checks if the current status of the device is :closed
|
39
|
+
#
|
40
|
+
def closed?
|
41
|
+
status == :closed
|
42
|
+
end
|
43
|
+
|
44
|
+
# returns the current queue. should be used for debugging only.
|
45
|
+
#
|
46
|
+
def queue
|
47
|
+
@queue.dup.freeze
|
53
48
|
end
|
54
49
|
|
55
50
|
class << self
|
@@ -58,114 +53,132 @@ module Sound
|
|
58
53
|
# direction is reading or writing or both
|
59
54
|
# format is MIDI vs PCM or others
|
60
55
|
# this method can take a block and if so closes the device after execution
|
61
|
-
|
62
|
-
|
63
|
-
puts "opening
|
56
|
+
#
|
57
|
+
def open(device = Device.new, direction = "w", &block)
|
58
|
+
puts "opening device: '#{device.id}'" if Sound.verbose
|
64
59
|
if block_given?
|
65
60
|
block.call(device)
|
66
61
|
device.close
|
67
62
|
else
|
68
|
-
device.
|
63
|
+
device.open
|
69
64
|
device
|
70
65
|
end
|
71
66
|
end
|
72
67
|
end
|
73
68
|
|
74
|
-
|
69
|
+
# opens the device.
|
70
|
+
#
|
71
|
+
def open
|
72
|
+
@status = :open
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
# writes data to the queue and immediately flushes the queue.
|
77
|
+
#
|
78
|
+
def play(data = Sound::Data.new)
|
75
79
|
write(data)
|
76
80
|
flush
|
77
81
|
end
|
78
|
-
|
79
|
-
|
82
|
+
|
83
|
+
# writes given Data to the queue as a data block thread. Threads pause
|
84
|
+
# after preperation, and during flushing they get started back up. If a
|
85
|
+
# thread isn't done preparing when flushed, it finished preparing and
|
86
|
+
# immediately writes data to the device.
|
87
|
+
#
|
88
|
+
def write(data = Sound::Data.new)
|
80
89
|
if closed?
|
81
90
|
puts "cannot write to a closed device"
|
82
91
|
else
|
83
92
|
@mutex.lock
|
84
93
|
@queue << Thread.new do
|
85
|
-
|
94
|
+
Thread.current[:async] = false
|
95
|
+
Thread.current[:data] = data
|
96
|
+
write_thread
|
86
97
|
end
|
87
98
|
@mutex.unlock
|
88
|
-
puts "writing to
|
99
|
+
puts "writing to queue of device '#{id}': #{data}" if Sound.verbose
|
89
100
|
end
|
101
|
+
self
|
90
102
|
end
|
91
103
|
|
104
|
+
# starts up data block threads that get played back at the same time. Need
|
105
|
+
# to make all threads wait until others are finished preparing the buffer.
|
106
|
+
#
|
107
|
+
def write_async(data = Sound::Data.new, new_queue_elem = false)
|
108
|
+
if closed?
|
109
|
+
puts "cannot write to a closed device"
|
110
|
+
else
|
111
|
+
@mutex.lock
|
112
|
+
if new_queue_elem || @queue.empty? || @queue.last.kind_of?(Thread)
|
113
|
+
threads = []
|
114
|
+
threads << Thread.new do
|
115
|
+
Thread.current[:async] = true
|
116
|
+
Thread.current[:data] = data
|
117
|
+
write_thread
|
118
|
+
end
|
119
|
+
@queue << threads
|
120
|
+
else
|
121
|
+
@queue.last << Thread.new do
|
122
|
+
Thread.current[:async] = true
|
123
|
+
Thread.current[:data] = data
|
124
|
+
write_thread
|
125
|
+
end
|
126
|
+
end
|
127
|
+
@mutex.unlock
|
128
|
+
puts "writing async to queue of device '#{id}': #{data}" if Sound.verbose
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# should make a close! method that ignores any pending queue data blocks,
|
133
|
+
# but still safely closes the device as quickly as possible.
|
134
|
+
#
|
92
135
|
def close
|
93
136
|
if closed?
|
94
137
|
puts "cannot close a closed device"
|
95
138
|
else
|
96
139
|
flush
|
97
|
-
puts "device is closing now" if Sound.verbose
|
98
|
-
|
140
|
+
puts "device '#{id}' is closing now" if Sound.verbose
|
141
|
+
@status = :closed
|
99
142
|
end
|
100
143
|
end
|
101
144
|
|
145
|
+
# flushes each block after previous finishes. Should make other options,
|
146
|
+
# like flush each block after a specified amount of time.
|
147
|
+
#
|
102
148
|
def flush
|
103
149
|
until @queue.empty?
|
104
150
|
output = @queue.shift
|
105
|
-
output
|
106
|
-
|
107
|
-
|
151
|
+
if output.kind_of? Thread
|
152
|
+
output[:stop] = false
|
153
|
+
puts "writing to device '#{id}': #{output[:data].class}" if Sound.verbose #this may be NilClass if parent thread is too fast
|
154
|
+
output.run.join
|
155
|
+
else
|
156
|
+
output.each do |thread|
|
157
|
+
thread[:stop] = false
|
158
|
+
puts "writing to device '#{id}': #{thread[:data].class}" if Sound.verbose
|
159
|
+
thread.run
|
160
|
+
end
|
161
|
+
output.last.join if output.last.alive?
|
162
|
+
end
|
108
163
|
end
|
109
164
|
end
|
110
165
|
|
111
|
-
|
112
|
-
closed
|
113
|
-
end
|
166
|
+
private
|
114
167
|
|
115
|
-
def write_thread
|
168
|
+
def write_thread
|
116
169
|
Thread.current[:stop] = true if Thread.current[:stop].nil?
|
117
|
-
if
|
118
|
-
|
119
|
-
|
120
|
-
|
170
|
+
if Sound.platform_supported
|
171
|
+
open_device
|
172
|
+
prepare_buffer
|
173
|
+
Thread.stop if Thread.current[:stop]
|
174
|
+
Thread.pass if Thread.current[:async]
|
175
|
+
write_to_device
|
176
|
+
unprepare_buffer
|
177
|
+
close_device
|
121
178
|
else
|
122
179
|
warn("warning: playback is not yet supported on this platform")
|
123
180
|
end
|
124
181
|
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
|
-
header = Win32::WAVEHDR.new(data_buffer, buffer_length)
|
133
|
-
Win32::Sound.waveOutPrepareHeader(handle.id, header.pointer, header.size)
|
134
|
-
Thread.stop if Thread.current[:stop]
|
135
|
-
Win32::Sound.waveOutWrite(handle.id, header.pointer, header.size)
|
136
|
-
while Win32::Sound.waveOutUnprepareHeader(handle.id, header.pointer, header.size) == 33
|
137
|
-
sleep 0.001
|
138
|
-
end
|
139
|
-
Win32::Sound.waveOutClose(handle.id)
|
140
|
-
end
|
141
|
-
|
142
|
-
def linux_write_thread(data)
|
143
|
-
handle = Handle.new
|
144
|
-
AlsaPCM::Sound.snd_pcm_open(handle.pointer, id, 0, 0)
|
145
|
-
data_buffer = FFI::MemoryPointer.new(:int, data.data.size)
|
146
|
-
data_buffer.write_array_of_int data.data
|
147
|
-
buffer_length = data_buffer.size/2
|
148
|
-
params = FFI::MemoryPointer.new(:pointer)
|
149
|
-
AlsaPCM::Sound.snd_pcm_hw_params_malloc(params)
|
150
|
-
AlsaPCM::Sound.snd_pcm_hw_params_any(handle.id, params.read_pointer)
|
151
|
-
|
152
|
-
AlsaPCM::Sound.snd_pcm_hw_params_set_access(handle.id, params.read_pointer, 3)
|
153
|
-
AlsaPCM::Sound.snd_pcm_hw_params_set_format(handle.id, params.read_pointer, 2)
|
154
|
-
AlsaPCM::Sound.snd_pcm_hw_params_set_rate(handle.id, params.read_pointer, 44100, 0)
|
155
|
-
AlsaPCM::Sound.snd_pcm_hw_params_set_channels(handle.id, params.read_pointer, 1)
|
156
|
-
|
157
|
-
AlsaPCM::Sound.snd_pcm_hw_params(handle.id, params.read_pointer)
|
158
|
-
AlsaPCM::Sound.snd_pcm_hw_params_free(params.read_pointer)
|
159
|
-
|
160
|
-
AlsaPCM::Sound.snd_pcm_prepare(handle.id)
|
161
|
-
Thread.stop if Thread.current[:stop]
|
162
|
-
AlsaPCM::Sound.snd_pcm_writei(handle.id, data_buffer, buffer_length)
|
163
|
-
|
164
|
-
|
165
|
-
AlsaPCM::Sound.snd_pcm_drain(handle.id)
|
166
|
-
AlsaPCM::Sound.snd_pcm_close(handle.id)
|
167
|
-
|
168
|
-
end
|
169
182
|
end
|
170
183
|
|
171
184
|
end
|
data/lib/sound/format.rb
CHANGED
@@ -5,8 +5,10 @@ module Sound
|
|
5
5
|
WAVE_FORMAT_PCM = 1
|
6
6
|
|
7
7
|
class Format
|
8
|
-
|
9
|
-
|
8
|
+
|
9
|
+
PCM = WAVE_FORMAT_PCM
|
10
|
+
attr_accessor :channels, :sample_rate, :bps, :alsa_format
|
11
|
+
def initialize(format_type = PCM)
|
10
12
|
@channels = 1
|
11
13
|
@sample_rate = 44100
|
12
14
|
@bps = 16
|
@@ -32,7 +34,6 @@ module Sound
|
|
32
34
|
@wfx.pointer
|
33
35
|
end
|
34
36
|
end
|
35
|
-
PCM = self.new
|
36
37
|
end
|
37
38
|
|
38
39
|
end
|
@@ -1,24 +1,79 @@
|
|
1
|
+
require 'ffi'
|
1
2
|
|
2
3
|
module Sound
|
3
4
|
module Win32
|
5
|
+
|
6
|
+
class Handle
|
7
|
+
def initialize
|
8
|
+
@handle = HWAVEOUT.new
|
9
|
+
end
|
10
|
+
def pointer
|
11
|
+
@handle.pointer
|
12
|
+
end
|
13
|
+
def id
|
14
|
+
@handle[:i]
|
15
|
+
end
|
16
|
+
end
|
4
17
|
|
5
|
-
|
6
|
-
extend FFI::Library
|
18
|
+
extend FFI::Library
|
7
19
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
ffi_lib :winmm
|
20
|
+
typedef :ulong, :dword
|
21
|
+
typedef :uintptr_t, :hwaveout
|
22
|
+
typedef :uint, :mmresult
|
13
23
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
24
|
+
ffi_lib :winmm
|
25
|
+
|
26
|
+
attach_function :waveOutOpen, [:pointer, :uint, :pointer, :dword, :dword, :dword], :mmresult
|
27
|
+
attach_function :waveOutPrepareHeader, [:hwaveout, :pointer, :uint], :mmresult
|
28
|
+
attach_function :waveOutWrite, [:hwaveout, :pointer, :uint], :mmresult
|
29
|
+
attach_function :waveOutUnprepareHeader, [:hwaveout, :pointer, :uint], :mmresult
|
30
|
+
attach_function :waveOutClose, [:hwaveout], :mmresult
|
20
31
|
|
21
32
|
WAVE_FORMAT_PCM = 1
|
33
|
+
WAVE_MAPPER = -1
|
34
|
+
DEFAULT_DEVICE_ID = WAVE_MAPPER
|
35
|
+
|
36
|
+
def open_device
|
37
|
+
waveOutOpen(handle.pointer, id, data.format.pointer, 0, 0, 0)
|
38
|
+
end
|
39
|
+
|
40
|
+
def prepare_buffer
|
41
|
+
waveOutPrepareHeader(handle.id, header.pointer, header.size)
|
42
|
+
end
|
43
|
+
|
44
|
+
def write_to_device
|
45
|
+
waveOutWrite(handle.id, header.pointer, header.size)
|
46
|
+
end
|
47
|
+
|
48
|
+
def unprepare_buffer
|
49
|
+
while waveOutUnprepareHeader(handle.id, header.pointer, header.size) == 33
|
50
|
+
sleep 0.001
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def close_device
|
55
|
+
waveOutClose(handle.id)
|
56
|
+
end
|
57
|
+
|
58
|
+
def handle
|
59
|
+
Thread.current[:handle] ||= Handle.new
|
60
|
+
end
|
61
|
+
|
62
|
+
def data
|
63
|
+
Thread.current[:data]
|
64
|
+
end
|
65
|
+
|
66
|
+
def header
|
67
|
+
Thread.current[:header] ||= WAVEHDR.new(data_buffer, buffer_length)
|
68
|
+
end
|
69
|
+
|
70
|
+
def data_buffer
|
71
|
+
Thread.current[:data_buffer] ||= FFI::MemoryPointer.new(:int, data.pcm_data.size).write_array_of_int data.pcm_data
|
72
|
+
end
|
73
|
+
|
74
|
+
def buffer_length
|
75
|
+
Thread.current[:buffer_length] ||= data.format.avg_bps*data.duration/1000
|
76
|
+
end
|
22
77
|
|
23
78
|
# Define an HWAVEOUT struct for use by all the waveOut functions.
|
24
79
|
# It is a handle to a waveOut stream, so starting up multiple
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
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.7
|
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-
|
12
|
-
dependencies:
|
11
|
+
date: 2014-08-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ffi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
13
41
|
description: Allows for effiecent cross-platform sound libraries in pure Ruby by tapping
|
14
42
|
into native libraries.
|
15
43
|
email: nicklink483@gmail.com
|
@@ -20,11 +48,11 @@ files:
|
|
20
48
|
- examples/example.rb
|
21
49
|
- lib/os/os.rb
|
22
50
|
- lib/sound.rb
|
51
|
+
- lib/sound/alsa.rb
|
23
52
|
- lib/sound/data.rb
|
24
53
|
- lib/sound/device.rb
|
25
54
|
- lib/sound/format.rb
|
26
|
-
- lib/sound/
|
27
|
-
- lib/sound/win32/sound.rb
|
55
|
+
- lib/sound/win32.rb
|
28
56
|
homepage: https://github.com/RSMP/sound
|
29
57
|
licenses:
|
30
58
|
- MIT
|
data/lib/sound/linux/sound.rb
DELETED
@@ -1,25 +0,0 @@
|
|
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
|