alsa-backup 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
data/.autotest ADDED
@@ -0,0 +1,4 @@
1
+ Autotest.add_hook :initialize do |autotest|
2
+ autotest.add_exception(/tmp/)
3
+ autotest.add_exception(/log/)
4
+ end
data/History.txt ADDED
@@ -0,0 +1,32 @@
1
+ == 0.0.7
2
+
3
+ * Make configurable channels, sample rate and alsa device
4
+
5
+ == 0.0.6
6
+
7
+ * Fix file writing problem
8
+
9
+ == 0.0.5
10
+
11
+ * Fix half buffer writing in recording file
12
+
13
+ == 0.0.4
14
+
15
+ * Replace :int by :off_t in Sndfile#write_int prototype
16
+ * Add logger in Sndfile
17
+ * Use a 500ms buffer in ALSA::Capture#read
18
+ * Add daemons in gem dependencies
19
+
20
+ == 0.0.3
21
+
22
+ * Support --background to daemonize process
23
+ * Support --pidfile option
24
+ * Rename existing file
25
+
26
+ == 0.0.2 2009-05-19
27
+
28
+ * Improve capture by support partial reads and recovering errors
29
+
30
+ == 0.0.1 2009-05-14
31
+
32
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,32 @@
1
+ .autotest
2
+ History.txt
3
+ Manifest.txt
4
+ PostInstall.txt
5
+ README.rdoc
6
+ Rakefile
7
+ TODO
8
+ alsa-backup.gemspec
9
+ bin/alsa-backup
10
+ config.sample
11
+ lib/alsa.rb
12
+ lib/alsa_backup.rb
13
+ lib/alsa_backup/cli.rb
14
+ lib/alsa_backup/core_ext.rb
15
+ lib/alsa_backup/length_controller.rb
16
+ lib/alsa_backup/recorder.rb
17
+ lib/alsa_backup/writer.rb
18
+ lib/sndfile.rb
19
+ script/console
20
+ script/destroy
21
+ script/generate
22
+ spec/alsa/pcm_spec.rb
23
+ spec/alsa_backup/cli_spec.rb
24
+ spec/alsa_backup/core_ext_spec.rb
25
+ spec/alsa_backup/recorder_spec.rb
26
+ spec/alsa_backup/writer_spec.rb
27
+ spec/alsa_backup_spec.rb
28
+ spec/fixtures/config_test.rb
29
+ spec/sndfile/info_spec.rb
30
+ spec/spec.opts
31
+ spec/spec_helper.rb
32
+ tasks/rspec.rake
data/PostInstall.txt ADDED
@@ -0,0 +1,7 @@
1
+
2
+ For more information on alsa_backup, see http://alsa_backup.rubyforge.org
3
+
4
+ NOTE: Change this information in PostInstall.txt
5
+ You can also delete it if you don't want it.
6
+
7
+
data/README.rdoc ADDED
@@ -0,0 +1,106 @@
1
+ = AlsaBackup
2
+
3
+ * http://github.com/alban.peignier/alsa-backup
4
+
5
+ == DESCRIPTION:
6
+
7
+ ALSA client to perform continuous recording
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * configurable file name strategy
12
+
13
+ == SYNOPSIS:
14
+
15
+ By using the default settings :
16
+
17
+ alsa-backup
18
+
19
+ will record in record.wav the default alsa device
20
+
21
+ By using command line arguments :
22
+
23
+ alsa-backup --directory=/tmp --file=test.wav --length=2
24
+
25
+ will record 2 seconds in /tmp/test.wav
26
+
27
+ By loading a configuration :
28
+
29
+ alsa-backup --config=/path/to/config
30
+
31
+ will load the specified configuration
32
+
33
+ == CONFIGURATION:
34
+
35
+ The configuration file is a Ruby file. This piece of code can
36
+ configurate the AlsaBacup recorder :
37
+
38
+ AlsaBackup.config do |recorder|
39
+ # configure recorder here :
40
+ recorder.file = "record.wav"
41
+ end
42
+
43
+ === Recorder File
44
+
45
+ The recorder file can be specified as a simple string :
46
+
47
+ recorder.file = "record.wav"
48
+
49
+ The recorder file can be specified by a Proc which returns the string :
50
+
51
+ recorder.file = Proc.new {
52
+ Time.now.strftime("%Y/%m-%b/%d-%a/%Hh.wav")
53
+ }
54
+
55
+ will use the current time to create file names like these :
56
+
57
+ 2009/05-May/17-Sun/19h.wav
58
+ 2009/05-May/17-Sun/20h.wav
59
+
60
+ To use different files every 15 minutes :
61
+
62
+ recorder.file = Proc.new {
63
+ Time.now.floor(:min, 15).strftime("%Y/%m-%b/%d-%a/%Hh%M.wav")
64
+ }
65
+
66
+ will create files like these :
67
+
68
+ 2009/05-May/17-Sun/19h00.wav
69
+ 2009/05-May/17-Sun/19h15.wav
70
+
71
+ See config.sample.
72
+
73
+ == REQUIREMENTS:
74
+
75
+ * alsa library (http://www.alsa-project.org/)
76
+ * libsndfile library (http://www.mega-nerd.com/libsndfile/)
77
+
78
+ == INSTALL:
79
+
80
+ sudo apt-get install libasound2 libsndfile1
81
+ sudo gem install --source http://gems.github.com albanpeignier-alsa-backup
82
+
83
+ == LICENSE:
84
+
85
+ (The MIT License)
86
+
87
+ Copyright (c) 2009 Alban Peignier
88
+
89
+ Permission is hereby granted, free of charge, to any person obtaining
90
+ a copy of this software and associated documentation files (the
91
+ 'Software'), to deal in the Software without restriction, including
92
+ without limitation the rights to use, copy, modify, merge, publish,
93
+ distribute, sublicense, and/or sell copies of the Software, and to
94
+ permit persons to whom the Software is furnished to do so, subject to
95
+ the following conditions:
96
+
97
+ The above copyright notice and this permission notice shall be
98
+ included in all copies or substantial portions of the Software.
99
+
100
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
101
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
102
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
103
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
104
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
105
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
106
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,65 @@
1
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
2
+ %w[rake rake/clean fileutils newgem hoe rubigen].each { |f| require f }
3
+ require File.dirname(__FILE__) + '/lib/alsa_backup'
4
+
5
+ # Generate all the Rake tasks
6
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
7
+ $hoe = Hoe.spec('alsa-backup') do |p|
8
+ p.version = AlsaBackup::VERSION
9
+ p.readme_file = 'README.rdoc'
10
+ p.developer('Alban Peignier', 'alban@tryphon.eu')
11
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
12
+ p.rubyforge_name = p.name # TODO this is default value
13
+ p.extra_deps = [
14
+ ['ffi','>= 0.3.5'], [ 'newgem', ">= #{::Newgem::VERSION}" ], [ 'daemons', '>= 1.0.10' ]
15
+ ]
16
+ p.extra_dev_deps = [
17
+ ['newgem', ">= #{::Newgem::VERSION}"]
18
+ ]
19
+ p.url = 'http://projects.tryphon.eu/alsa-backup'
20
+
21
+ p.clean_globs |= %w[**/.DS_Store tmp *.log]
22
+ path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
23
+ p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
24
+ p.rsync_args = '-av --delete --ignore-errors'
25
+ end
26
+
27
+ desc 'Recreate Manifest.txt to include ALL files'
28
+ task :manifest do
29
+ `rake check_manifest | patch -p0 > Manifest.txt`
30
+ end
31
+
32
+ desc "Generate a #{$hoe.name}.gemspec file"
33
+ task :gemspec do
34
+ File.open("#{$hoe.name}.gemspec", "w") do |file|
35
+ file.puts $hoe.spec.to_ruby
36
+ end
37
+ end
38
+
39
+ require 'newgem/tasks' # load /tasks/*.rake
40
+ Dir['tasks/**/*.rake'].each { |t| load t }
41
+
42
+ task :default => :spec
43
+
44
+ namespace :gems do
45
+ task :install do
46
+ gems = %w{active_support ffi rspec daemons}
47
+ sh "sudo gem install #{gems.join(' ')}"
48
+ end
49
+ end
50
+
51
+ require 'debian/build'
52
+
53
+ include Debian::Build
54
+ require 'debian/build/config'
55
+
56
+ namespace "package" do
57
+ Package.new(:"alsa-backup") do |t|
58
+ t.version = '0.0.8'
59
+ t.debian_increment = 1
60
+
61
+ t.source_provider = GitExportProvider.new
62
+ end
63
+ end
64
+
65
+ require 'debian/build/tasks'
data/TODO ADDED
@@ -0,0 +1,3 @@
1
+ * check write permissions of directories created by Writer
2
+ * replace ALSA::try_to blocks by Proxy with method_missing
3
+ * improve spec coverage
@@ -0,0 +1,47 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{alsa-backup}
5
+ s.version = "0.0.8"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Alban Peignier"]
9
+ s.date = %q{2010-01-23}
10
+ s.default_executable = %q{alsa-backup}
11
+ s.description = %q{ALSA client to perform continuous recording}
12
+ s.email = ["alban@tryphon.eu"]
13
+ s.executables = ["alsa-backup"]
14
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt", "PostInstall.txt"]
15
+ s.files = [".autotest", "History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc", "Rakefile", "TODO", "alsa-backup.gemspec", "bin/alsa-backup", "config.sample", "lib/alsa.rb", "lib/alsa_backup.rb", "lib/alsa_backup/cli.rb", "lib/alsa_backup/core_ext.rb", "lib/alsa_backup/length_controller.rb", "lib/alsa_backup/recorder.rb", "lib/alsa_backup/writer.rb", "lib/sndfile.rb", "script/console", "script/destroy", "script/generate", "spec/alsa/pcm_spec.rb", "spec/alsa_backup/cli_spec.rb", "spec/alsa_backup/core_ext_spec.rb", "spec/alsa_backup/recorder_spec.rb", "spec/alsa_backup/writer_spec.rb", "spec/alsa_backup_spec.rb", "spec/fixtures/config_test.rb", "spec/sndfile/info_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/rspec.rake"]
16
+ s.homepage = %q{http://projects.tryphon.eu/alsa-backup}
17
+ s.rdoc_options = ["--main", "README.rdoc"]
18
+ s.require_paths = ["lib"]
19
+ s.rubyforge_project = %q{alsa-backup}
20
+ s.rubygems_version = %q{1.3.5}
21
+ s.summary = %q{ALSA client to perform continuous recording}
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 3
26
+
27
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
+ s.add_runtime_dependency(%q<ffi>, [">= 0.3.5"])
29
+ s.add_runtime_dependency(%q<newgem>, [">= 1.5.2"])
30
+ s.add_runtime_dependency(%q<daemons>, [">= 1.0.10"])
31
+ s.add_development_dependency(%q<newgem>, [">= 1.5.2"])
32
+ s.add_development_dependency(%q<hoe>, [">= 2.5.0"])
33
+ else
34
+ s.add_dependency(%q<ffi>, [">= 0.3.5"])
35
+ s.add_dependency(%q<newgem>, [">= 1.5.2"])
36
+ s.add_dependency(%q<daemons>, [">= 1.0.10"])
37
+ s.add_dependency(%q<newgem>, [">= 1.5.2"])
38
+ s.add_dependency(%q<hoe>, [">= 2.5.0"])
39
+ end
40
+ else
41
+ s.add_dependency(%q<ffi>, [">= 0.3.5"])
42
+ s.add_dependency(%q<newgem>, [">= 1.5.2"])
43
+ s.add_dependency(%q<daemons>, [">= 1.0.10"])
44
+ s.add_dependency(%q<newgem>, [">= 1.5.2"])
45
+ s.add_dependency(%q<hoe>, [">= 2.5.0"])
46
+ end
47
+ end
data/bin/alsa-backup ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created on 2009-5-14.
4
+ # Copyright (c) 2009. All rights reserved.
5
+
6
+ require 'alsa_backup'
7
+ require 'alsa_backup/cli'
8
+
9
+ AlsaBackup::CLI.execute(STDOUT, *ARGV)
data/config.sample ADDED
@@ -0,0 +1,91 @@
1
+ #
2
+ # Config sample for Alsa.Backup
3
+ #
4
+ # Usage:
5
+ # alsa-backup --config /path/to/this/file
6
+ #
7
+
8
+ #
9
+ # To use syslog
10
+ #
11
+ #require 'syslog_logger'
12
+ #AlsaBackup.logger = SyslogLogger.new('alsa-backup').tap do |logger|
13
+ # logger.level = Logger::INFO
14
+ #end
15
+
16
+ #
17
+ # To debug
18
+ #
19
+ #Sndfile::logger = ALSA::logger = AlsaBackup.logger = Logger.new(STDOUT)
20
+
21
+ AlsaBackup.logger.info("load config #{__FILE__}")
22
+
23
+ AlsaBackup.config do |recorder|
24
+
25
+ #
26
+ # To specify the alsa device to be used
27
+ #
28
+ #recorder.device = "plughw:0"
29
+
30
+ #
31
+ # To specify the channel count to be used
32
+ #
33
+ #recorder.channels = 1
34
+ #recorder.channels = 4
35
+
36
+ #
37
+ # To specify the sample_rate to be used
38
+ #
39
+ #recorder.sample_rate = 48000
40
+
41
+ #
42
+ # To use files named like this :
43
+ # - 2009/05-May/17-Sun/19h.wav
44
+ # - 2009/05-May/17-Sun/20h.wav
45
+ #
46
+ #recorder.file = Proc.new {
47
+ # Time.now.strftime("%Y/%m-%b/%d-%a/%Hh.wav")
48
+ #}
49
+
50
+ #
51
+ # To use files named like this :
52
+ # - 2009/05-May/17-Sun/19h00.wav
53
+ # - 2009/05-May/17-Sun/19h15.wav
54
+ #
55
+ #recorder.file = Proc.new {
56
+ # Time.now.floor(:min, 15).strftime("%Y/%m-%b/%d-%a/%Hh%M.wav")
57
+ #}
58
+
59
+ #
60
+ # Define where the files are created
61
+ #
62
+ #recorder.directory = "/var/lib/alsa-backup"
63
+
64
+ #
65
+ # Customize error handler during record
66
+ #
67
+
68
+ # To sleep 30 seconds on error
69
+ #recorder.error_handler = Proc.new do |exception|
70
+ # 30
71
+ #end
72
+
73
+ # To fail on any error
74
+ #recorder.error_handler = Proc.new do |exception|
75
+ # false
76
+ #end
77
+
78
+ # To sleep the default time
79
+ #recorder.error_handler = Proc.new do |exception|
80
+ # true
81
+ #end
82
+
83
+ # To process created files
84
+ # WARN: don't forget the & in the command line or fork
85
+ #recorder.on_close do |file|
86
+ # AlsaBackup.logger.info "encode in ogg : #{file}"
87
+ # system "oggenc -Q -q7 #{file} &"
88
+ #end
89
+ end
90
+
91
+
data/lib/alsa.rb ADDED
@@ -0,0 +1,299 @@
1
+ require 'ffi'
2
+
3
+ require 'logger'
4
+
5
+ module ALSA
6
+
7
+ def self.logger
8
+ unless @logger
9
+ @logger = Logger.new(STDERR)
10
+ @logger.level = Logger::WARN
11
+ end
12
+
13
+ @logger
14
+ end
15
+
16
+ def self.logger=(logger); @logger = logger; end
17
+
18
+ def self.try_to(message, &block)
19
+ logger.debug { message }
20
+ if ALSA::Native::error_code?(response = yield)
21
+ raise "cannot #{message} (#{ALSA::Native::strerror(response)})"
22
+ else
23
+ response
24
+ end
25
+ end
26
+
27
+ module Native
28
+ extend FFI::Library
29
+ ffi_lib "libasound.so"
30
+
31
+ attach_function :strerror, :snd_strerror, [:int], :string
32
+
33
+ def self.error_code?(response)
34
+ response < 0
35
+ end
36
+ end
37
+
38
+ module PCM
39
+
40
+ class Capture
41
+
42
+ attr_accessor :handle
43
+
44
+ def self.open(device, hardware_attributes = {}, &block)
45
+ Capture.new.open(device, hardware_attributes, &block)
46
+ end
47
+
48
+ def open(device, hardware_attributes = {}, &block)
49
+ capture_handle = FFI::MemoryPointer.new :pointer
50
+ ALSA::try_to "open audio device #{device}" do
51
+ ALSA::PCM::Native::open capture_handle, device, ALSA::PCM::Native::STREAM_CAPTURE, ALSA::PCM::Native::BLOCK
52
+ end
53
+ self.handle = capture_handle.read_pointer
54
+
55
+ self.hardware_parameters=hardware_attributes
56
+
57
+ if block_given?
58
+ begin
59
+ yield self
60
+ ensure
61
+ self.close
62
+ end
63
+ end
64
+ end
65
+
66
+ def change_hardware_parameters
67
+ hw_params = HwParameters.new(self).default_for_device
68
+
69
+ begin
70
+ yield hw_params
71
+
72
+ ALSA::try_to "set hw parameters" do
73
+ ALSA::PCM::Native::hw_params self.handle, hw_params.handle
74
+ end
75
+ ensure
76
+ hw_params.free
77
+ end
78
+ end
79
+
80
+ def hardware_parameters
81
+ HwParameters.new(self).current_for_device
82
+ end
83
+ alias_method :hw_params, :hardware_parameters
84
+
85
+ def hardware_parameters=(attributes= {})
86
+ attributes = {:access => :rw_interleaved}.update(attributes)
87
+ change_hardware_parameters do |hw_params|
88
+ hw_params.update_attributes(attributes)
89
+ end
90
+ end
91
+
92
+ def read
93
+ ALSA.logger.debug { "start read with #{hw_params.sample_rate}, #{hw_params.channels} channels"}
94
+
95
+ # use an 500ms buffer
96
+ frame_count = hw_params.sample_rate / 2
97
+ ALSA.logger.debug { "allocate #{hw_params.buffer_size_for(frame_count)} bytes for #{frame_count} frames" }
98
+ FFI::MemoryPointer.new(:char, hw_params.buffer_size_for(frame_count)) do |buffer|
99
+ begin
100
+ read_buffer buffer, frame_count
101
+ end while yield buffer, frame_count
102
+ end
103
+ end
104
+
105
+ def read_buffer(buffer, frame_count)
106
+ read_count = ALSA::try_to "read from audio interface" do
107
+ response = ALSA::PCM::Native::readi(self.handle, buffer, frame_count)
108
+ if ALSA::Native::error_code?(response)
109
+ ALSA.logger.warn { "try to recover '#{ALSA::Native::strerror(response)}' on read"}
110
+ ALSA::PCM::Native::pcm_recover(self.handle, response, 1)
111
+ else
112
+ response
113
+ end
114
+ end
115
+
116
+ missing_frame_count = frame_count - read_count
117
+ if missing_frame_count > 0
118
+ ALSA.logger.debug { "re-read missing frame count: #{missing_frame_count}"}
119
+ read_buffer_size = hw_params.buffer_size_for(read_count)
120
+ # buffer[read_buffer_size] doesn't return a MemoryPointer
121
+ read_buffer(buffer + read_buffer_size, missing_frame_count)
122
+ end
123
+ end
124
+
125
+ def close
126
+ ALSA::try_to "close audio device" do
127
+ ALSA::PCM::Native::close self.handle
128
+ end
129
+ end
130
+
131
+ class HwParameters
132
+
133
+ attr_accessor :handle, :device
134
+
135
+ def initialize(device = nil)
136
+ hw_params_pointer = FFI::MemoryPointer.new :pointer
137
+
138
+ ALSA::PCM::Native::hw_params_malloc hw_params_pointer
139
+ self.handle = hw_params_pointer.read_pointer
140
+
141
+ self.device = device if device
142
+ end
143
+
144
+ def update_attributes(attributes)
145
+ attributes.each_pair { |name, value| send("#{name}=", value) }
146
+ end
147
+
148
+ def default_for_device
149
+ ALSA::try_to "initialize hardware parameter structure" do
150
+ ALSA::PCM::Native::hw_params_any device.handle, self.handle
151
+ end
152
+ self
153
+ end
154
+
155
+ def current_for_device
156
+ ALSA::try_to "retrieve current hardware parameters" do
157
+ ALSA::PCM::Native::hw_params_current device.handle, self.handle
158
+ end
159
+ self
160
+ end
161
+
162
+ def access=(access)
163
+ ALSA::try_to "set access type" do
164
+ ALSA::PCM::Native::hw_params_set_access self.device.handle, self.handle, ALSA::PCM::Native::Access.const_get(access.to_s.upcase)
165
+ end
166
+ end
167
+
168
+ def channels=(channels)
169
+ ALSA::try_to "set channel count : #{channels}" do
170
+ ALSA::PCM::Native::hw_params_set_channels self.device.handle, self.handle, channels
171
+ end
172
+ end
173
+
174
+ def sample_rate=(sample_rate)
175
+ ALSA::try_to "set sample rate" do
176
+ rate = FFI::MemoryPointer.new(:int)
177
+ rate.write_int(sample_rate)
178
+
179
+ dir = FFI::MemoryPointer.new(:int)
180
+ dir.write_int(0)
181
+
182
+ error_code = ALSA::PCM::Native::hw_params_set_rate_near self.device.handle, self.handle, rate, dir
183
+
184
+ rate.free
185
+ dir.free
186
+
187
+ error_code
188
+ end
189
+ end
190
+
191
+ def sample_rate
192
+ rate = nil
193
+ ALSA::try_to "get sample rate" do
194
+ rate_pointer = FFI::MemoryPointer.new(:int)
195
+ dir_pointer = FFI::MemoryPointer.new(:int)
196
+ dir_pointer.write_int(0)
197
+
198
+ error_code = ALSA::PCM::Native::hw_params_get_rate self.handle, rate_pointer, dir_pointer
199
+
200
+ rate = rate_pointer.read_int
201
+
202
+ rate_pointer.free
203
+ dir_pointer.free
204
+
205
+ error_code
206
+ end
207
+ rate
208
+ end
209
+
210
+ def sample_format=(sample_format)
211
+ ALSA::try_to "set sample format" do
212
+ ALSA::PCM::Native::hw_params_set_format self.device.handle, self.handle, ALSA::PCM::Native::Format.const_get(sample_format.to_s.upcase)
213
+ end
214
+ end
215
+
216
+ def sample_format
217
+ format = nil
218
+ FFI::MemoryPointer.new(:int) do |format_pointer|
219
+ ALSA::try_to "get sample format" do
220
+ ALSA::PCM::Native::hw_params_get_format self.handle, format_pointer
221
+ end
222
+ format = format_pointer.read_int
223
+ end
224
+ format
225
+ end
226
+
227
+ def channels
228
+ channels = nil
229
+ FFI::MemoryPointer.new(:int) do |channels_pointer|
230
+ ALSA::try_to "get channels" do
231
+ ALSA::PCM::Native::hw_params_get_channels self.handle, channels_pointer
232
+ end
233
+ channels = channels_pointer.read_int
234
+ end
235
+ channels
236
+ end
237
+
238
+ def buffer_size_for(frame_count)
239
+ ALSA::PCM::Native::format_size(self.sample_format, frame_count) * self.channels
240
+ end
241
+
242
+ def free
243
+ ALSA::try_to "unallocate hw_params" do
244
+ ALSA::PCM::Native::hw_params_free self.handle
245
+ end
246
+ end
247
+
248
+ end
249
+
250
+ end
251
+
252
+ module Native
253
+ extend FFI::Library
254
+ ffi_lib "libasound.so"
255
+
256
+ STREAM_CAPTURE = 1
257
+ BLOCK = 0
258
+ attach_function :open, :snd_pcm_open, [:pointer, :string, :int, :int], :int
259
+ attach_function :prepare, :snd_pcm_prepare, [ :pointer ], :int
260
+ attach_function :close, :snd_pcm_close, [:pointer], :int
261
+
262
+ attach_function :readi, :snd_pcm_readi, [ :pointer, :pointer, :ulong ], :long
263
+
264
+ attach_function :pcm_recover, :snd_pcm_recover, [ :pointer, :int, :int ], :int
265
+
266
+ attach_function :hw_params_malloc, :snd_pcm_hw_params_malloc, [:pointer], :int
267
+ attach_function :hw_params_free, :snd_pcm_hw_params_free, [:pointer], :int
268
+
269
+ attach_function :hw_params, :snd_pcm_hw_params, [ :pointer, :pointer ], :int
270
+ attach_function :hw_params_any, :snd_pcm_hw_params_any, [:pointer, :pointer], :int
271
+ attach_function :hw_params_current, :snd_pcm_hw_params_current, [ :pointer, :pointer ], :int
272
+
273
+
274
+ module Access
275
+ MMAP_INTERLEAVED = 0
276
+ MMAP_NONINTERLEAVED = 1
277
+ MMAP_COMPLEX = 2
278
+ RW_INTERLEAVED = 3
279
+ RW_NONINTERLEAVED = 4
280
+ end
281
+
282
+ attach_function :hw_params_set_access, :snd_pcm_hw_params_set_access, [ :pointer, :pointer, :int ], :int
283
+
284
+ module Format
285
+ S16_LE = 2
286
+ end
287
+
288
+ attach_function :hw_params_set_format, :snd_pcm_hw_params_set_format, [ :pointer, :pointer, :int ], :int
289
+ attach_function :hw_params_get_format, :snd_pcm_hw_params_get_format, [ :pointer, :pointer ], :int
290
+ attach_function :hw_params_get_rate, :snd_pcm_hw_params_get_rate, [ :pointer, :pointer, :pointer ], :int
291
+ attach_function :hw_params_set_rate_near, :snd_pcm_hw_params_set_rate_near, [ :pointer, :pointer, :pointer, :pointer ], :int
292
+ attach_function :hw_params_set_channels, :snd_pcm_hw_params_set_channels, [ :pointer, :pointer, :uint ], :int
293
+ attach_function :hw_params_get_channels, :snd_pcm_hw_params_get_format, [ :pointer, :pointer ], :int
294
+ attach_function :hw_params_set_periods, :snd_pcm_hw_params_set_periods, [ :pointer, :pointer, :uint, :int ], :int
295
+
296
+ attach_function :format_size, :snd_pcm_format_size, [ :int, :uint ], :int
297
+ end
298
+ end
299
+ end