alsa-backup 0.0.8

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.
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