albanpeignier-alsa-backup 0.0.1
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 +3 -0
- data/History.txt +4 -0
- data/Manifest.txt +26 -0
- data/PostInstall.txt +7 -0
- data/README.rdoc +72 -0
- data/Rakefile +27 -0
- data/alsa-backup.gemspec +42 -0
- data/bin/alsa.backup +11 -0
- data/config.sample +43 -0
- data/lib/alsa.rb +278 -0
- data/lib/alsa_backup.rb +48 -0
- data/lib/alsa_backup/cli.rb +44 -0
- data/lib/alsa_backup/recorder.rb +71 -0
- data/lib/sndfile.rb +121 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/alsa/pcm_spec.rb +7 -0
- data/spec/alsa_backup/cli_spec.rb +62 -0
- data/spec/alsa_backup/recorder_spec.rb +31 -0
- data/spec/alsa_backup_spec.rb +11 -0
- data/spec/fixtures/config_test.rb +3 -0
- data/spec/sndfile/info_spec.rb +38 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +21 -0
- data/tasks/rspec.rake +21 -0
- metadata +112 -0
data/.autotest
ADDED
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
.autotest
|
2
|
+
History.txt
|
3
|
+
Manifest.txt
|
4
|
+
PostInstall.txt
|
5
|
+
README.rdoc
|
6
|
+
Rakefile
|
7
|
+
alsa-backup.gemspec
|
8
|
+
bin/alsa.backup
|
9
|
+
config.sample
|
10
|
+
lib/alsa.rb
|
11
|
+
lib/alsa_backup.rb
|
12
|
+
lib/alsa_backup/cli.rb
|
13
|
+
lib/alsa_backup/recorder.rb
|
14
|
+
lib/sndfile.rb
|
15
|
+
script/console
|
16
|
+
script/destroy
|
17
|
+
script/generate
|
18
|
+
spec/alsa/pcm_spec.rb
|
19
|
+
spec/alsa_backup/cli_spec.rb
|
20
|
+
spec/alsa_backup/recorder_spec.rb
|
21
|
+
spec/alsa_backup_spec.rb
|
22
|
+
spec/fixtures/config_test.rb
|
23
|
+
spec/sndfile/info_spec.rb
|
24
|
+
spec/spec.opts
|
25
|
+
spec/spec_helper.rb
|
26
|
+
tasks/rspec.rake
|
data/PostInstall.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,72 @@
|
|
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 --directoy=/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
|
+
See config.sample for the moment.
|
36
|
+
|
37
|
+
== REQUIREMENTS:
|
38
|
+
|
39
|
+
* alsa library (http://www.alsa-project.org/)
|
40
|
+
* libsndfile library (http://www.mega-nerd.com/libsndfile/)
|
41
|
+
|
42
|
+
== INSTALL:
|
43
|
+
|
44
|
+
* sudo apt-get install libasound2 libsndfile1
|
45
|
+
* sudo gem install ruby-ffi
|
46
|
+
|
47
|
+
* sudo gem install --source http://gems.github.com albanpeignier-alsa-backup
|
48
|
+
|
49
|
+
== LICENSE:
|
50
|
+
|
51
|
+
(The MIT License)
|
52
|
+
|
53
|
+
Copyright (c) 2009 FIXME Alban Peignier
|
54
|
+
|
55
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
56
|
+
a copy of this software and associated documentation files (the
|
57
|
+
'Software'), to deal in the Software without restriction, including
|
58
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
59
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
60
|
+
permit persons to whom the Software is furnished to do so, subject to
|
61
|
+
the following conditions:
|
62
|
+
|
63
|
+
The above copyright notice and this permission notice shall be
|
64
|
+
included in all copies or substantial portions of the Software.
|
65
|
+
|
66
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
67
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
68
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
69
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
70
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
71
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
72
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
2
|
+
%w[rake rake/clean fileutils newgem 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.new('alsa-backup', AlsaBackup::VERSION) do |p|
|
8
|
+
p.developer('Alban Peignier', 'alban.peignier@free.fr')
|
9
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
10
|
+
p.rubyforge_name = p.name # TODO this is default value
|
11
|
+
p.extra_deps = [
|
12
|
+
['ffi','>= 0.3.5'],
|
13
|
+
]
|
14
|
+
p.extra_dev_deps = [
|
15
|
+
['newgem', ">= #{::Newgem::VERSION}"]
|
16
|
+
]
|
17
|
+
|
18
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
19
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
20
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
21
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
25
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
26
|
+
|
27
|
+
task :default => :spec
|
data/alsa-backup.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{alsa-backup}
|
5
|
+
s.version = "0.0.1"
|
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{2009-05-18}
|
10
|
+
s.default_executable = %q{alsa.backup}
|
11
|
+
s.description = %q{ALSA client to perform continuous recording}
|
12
|
+
s.email = ["alban.peignier@free.fr"]
|
13
|
+
s.executables = ["alsa.backup"]
|
14
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc"]
|
15
|
+
s.files = [".autotest", "History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc", "Rakefile", "alsa-backup.gemspec", "bin/alsa.backup", "config.sample", "lib/alsa.rb", "lib/alsa_backup.rb", "lib/alsa_backup/cli.rb", "lib/alsa_backup/recorder.rb", "lib/sndfile.rb", "script/console", "script/destroy", "script/generate", "spec/alsa/pcm_spec.rb", "spec/alsa_backup/cli_spec.rb", "spec/alsa_backup/recorder_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.has_rdoc = true
|
17
|
+
s.homepage = %q{http://github.com/alban.peignier/alsa-backup}
|
18
|
+
s.rdoc_options = ["--main", "README.rdoc"]
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
s.rubyforge_project = %q{alsa-backup}
|
21
|
+
s.rubygems_version = %q{1.3.2}
|
22
|
+
s.summary = %q{ALSA client to perform continuous recording}
|
23
|
+
|
24
|
+
if s.respond_to? :specification_version then
|
25
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
26
|
+
s.specification_version = 3
|
27
|
+
|
28
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
29
|
+
s.add_runtime_dependency(%q<ffi>, [">= 0.3.5"])
|
30
|
+
s.add_development_dependency(%q<newgem>, [">= 1.4.1"])
|
31
|
+
s.add_development_dependency(%q<hoe>, [">= 1.8.0"])
|
32
|
+
else
|
33
|
+
s.add_dependency(%q<ffi>, [">= 0.3.5"])
|
34
|
+
s.add_dependency(%q<newgem>, [">= 1.4.1"])
|
35
|
+
s.add_dependency(%q<hoe>, [">= 1.8.0"])
|
36
|
+
end
|
37
|
+
else
|
38
|
+
s.add_dependency(%q<ffi>, [">= 0.3.5"])
|
39
|
+
s.add_dependency(%q<newgem>, [">= 1.4.1"])
|
40
|
+
s.add_dependency(%q<hoe>, [">= 1.8.0"])
|
41
|
+
end
|
42
|
+
end
|
data/bin/alsa.backup
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Created on 2009-5-14.
|
4
|
+
# Copyright (c) 2009. All rights reserved.
|
5
|
+
|
6
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/alsa_backup")
|
7
|
+
|
8
|
+
require 'alsa_backup'
|
9
|
+
require 'alsa_backup/cli'
|
10
|
+
|
11
|
+
AlsaBackup::CLI.execute(STDOUT, *ARGV)
|
data/config.sample
ADDED
@@ -0,0 +1,43 @@
|
|
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
|
+
AlsaBackup.logger.info("load config #{__FILE__}")
|
17
|
+
|
18
|
+
AlsaBackup.config do |recorder|
|
19
|
+
#
|
20
|
+
# To use files named like this :
|
21
|
+
# - 2009/05-May/17-Sun/19h.wav
|
22
|
+
# - 2009/05-May/17-Sun/20h.wav
|
23
|
+
#
|
24
|
+
#recorder.file = Proc.new {
|
25
|
+
# Time.now.strftime("%Y/%m-%b/%d-%a/%Hh.wav")
|
26
|
+
#}
|
27
|
+
|
28
|
+
#
|
29
|
+
# To use files named like this :
|
30
|
+
# - 2009/05-May/17-Sun/19h00.wav
|
31
|
+
# - 2009/05-May/17-Sun/19h15.wav
|
32
|
+
#
|
33
|
+
#recorder.file = Proc.new {
|
34
|
+
# Time.now.floor(:min, 15).strftime("%Y/%m-%b/%d-%a/%Hh%M.wav")
|
35
|
+
#}
|
36
|
+
|
37
|
+
#
|
38
|
+
# Define where the files are created
|
39
|
+
#
|
40
|
+
#recorder.directory = "/var/lib/alsa.backup"
|
41
|
+
end
|
42
|
+
|
43
|
+
|
data/lib/alsa.rb
ADDED
@@ -0,0 +1,278 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
module ALSA
|
7
|
+
|
8
|
+
def self.logger
|
9
|
+
unless @logger
|
10
|
+
@logger = Logger.new(STDERR)
|
11
|
+
@logger.level = Logger::WARN
|
12
|
+
end
|
13
|
+
|
14
|
+
@logger
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.logger=(logger); @logger = logger; end
|
18
|
+
|
19
|
+
def self.try_to(message, &block)
|
20
|
+
logger.debug('alsa') { message }
|
21
|
+
if (response = yield) < 0
|
22
|
+
raise "cannot #{message} (#{ALSA::Native::strerror(response)})"
|
23
|
+
else
|
24
|
+
response
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module Native
|
29
|
+
extend FFI::Library
|
30
|
+
ffi_lib "libasound.so"
|
31
|
+
|
32
|
+
attach_function :strerror, :snd_strerror, [:int], :string
|
33
|
+
end
|
34
|
+
|
35
|
+
module PCM
|
36
|
+
|
37
|
+
class Capture
|
38
|
+
|
39
|
+
attr_accessor :handle
|
40
|
+
|
41
|
+
def self.open(device, hardware_attributes = {}, &block)
|
42
|
+
Capture.new.open(device, hardware_attributes, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def open(device, hardware_attributes = {}, &block)
|
46
|
+
capture_handle = FFI::MemoryPointer.new :pointer
|
47
|
+
ALSA::try_to "open audio device #{device}" do
|
48
|
+
ALSA::PCM::Native::open capture_handle, device, ALSA::PCM::Native::STREAM_CAPTURE, ALSA::PCM::Native::BLOCK
|
49
|
+
end
|
50
|
+
self.handle = capture_handle.read_pointer
|
51
|
+
|
52
|
+
self.hardware_parameters=hardware_attributes
|
53
|
+
|
54
|
+
if block_given?
|
55
|
+
begin
|
56
|
+
yield self
|
57
|
+
ensure
|
58
|
+
self.close
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def change_hardware_parameters
|
64
|
+
hw_params = HwParameters.new(self).default_for_device
|
65
|
+
|
66
|
+
begin
|
67
|
+
yield hw_params
|
68
|
+
|
69
|
+
ALSA::try_to "set hw parameters" do
|
70
|
+
ALSA::PCM::Native::hw_params self.handle, hw_params.handle
|
71
|
+
end
|
72
|
+
ensure
|
73
|
+
hw_params.free
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def hardware_parameters
|
78
|
+
HwParameters.new(self).current_for_device
|
79
|
+
end
|
80
|
+
|
81
|
+
def hardware_parameters=(attributes= {})
|
82
|
+
attributes = {:access => :rw_interleaved}.update(attributes)
|
83
|
+
change_hardware_parameters do |hw_params|
|
84
|
+
hw_params.update_attributes(attributes)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def read
|
89
|
+
frame_count = hardware_parameters.sample_rate
|
90
|
+
buffer = FFI::MemoryPointer.new(hardware_parameters.buffer_size_for(frame_count))
|
91
|
+
|
92
|
+
continue = true
|
93
|
+
while continue
|
94
|
+
read_count = ALSA::try_to "read from audio interface" do
|
95
|
+
ALSA::PCM::Native::readi(self.handle, buffer, frame_count)
|
96
|
+
end
|
97
|
+
|
98
|
+
raise "can't read expected frame count (#{read_count}/#{frame_count})" unless read_count == frame_count
|
99
|
+
|
100
|
+
continue = yield buffer, read_count
|
101
|
+
end
|
102
|
+
|
103
|
+
buffer.free
|
104
|
+
end
|
105
|
+
|
106
|
+
def close
|
107
|
+
ALSA::try_to "close audio device" do
|
108
|
+
ALSA::PCM::Native::close self.handle
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class HwParameters
|
113
|
+
|
114
|
+
attr_accessor :handle, :device
|
115
|
+
|
116
|
+
def initialize(device = nil)
|
117
|
+
hw_params_pointer = FFI::MemoryPointer.new :pointer
|
118
|
+
|
119
|
+
ALSA::PCM::Native::hw_params_malloc hw_params_pointer
|
120
|
+
self.handle = hw_params_pointer.read_pointer
|
121
|
+
|
122
|
+
self.device = device if device
|
123
|
+
end
|
124
|
+
|
125
|
+
def update_attributes(attributes)
|
126
|
+
attributes.each_pair { |name, value| send("#{name}=", value) }
|
127
|
+
end
|
128
|
+
|
129
|
+
def default_for_device
|
130
|
+
ALSA::try_to "initialize hardware parameter structure" do
|
131
|
+
ALSA::PCM::Native::hw_params_any device.handle, self.handle
|
132
|
+
end
|
133
|
+
self
|
134
|
+
end
|
135
|
+
|
136
|
+
def current_for_device
|
137
|
+
ALSA::try_to "retrieve current hardware parameters" do
|
138
|
+
ALSA::PCM::Native::hw_params_current device.handle, self.handle
|
139
|
+
end
|
140
|
+
self
|
141
|
+
end
|
142
|
+
|
143
|
+
def access=(access)
|
144
|
+
ALSA::try_to "set access type" do
|
145
|
+
ALSA::PCM::Native::hw_params_set_access self.device.handle, self.handle, ALSA::PCM::Native::Access.const_get(access.to_s.upcase)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def channels=(channels)
|
150
|
+
ALSA::try_to "set channel count : #{channels}" do
|
151
|
+
ALSA::PCM::Native::hw_params_set_channels self.device.handle, self.handle, channels
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def sample_rate=(sample_rate)
|
156
|
+
ALSA::try_to "set sample rate" do
|
157
|
+
rate = FFI::MemoryPointer.new(:int)
|
158
|
+
rate.write_int(sample_rate)
|
159
|
+
|
160
|
+
dir = FFI::MemoryPointer.new(:int)
|
161
|
+
dir.write_int(0)
|
162
|
+
|
163
|
+
error_code = ALSA::PCM::Native::hw_params_set_rate_near self.device.handle, self.handle, rate, dir
|
164
|
+
|
165
|
+
rate.free
|
166
|
+
dir.free
|
167
|
+
|
168
|
+
error_code
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def sample_rate
|
173
|
+
rate = nil
|
174
|
+
ALSA::try_to "get sample rate" do
|
175
|
+
rate_pointer = FFI::MemoryPointer.new(:int)
|
176
|
+
dir_pointer = FFI::MemoryPointer.new(:int)
|
177
|
+
dir_pointer.write_int(0)
|
178
|
+
|
179
|
+
error_code = ALSA::PCM::Native::hw_params_get_rate self.handle, rate_pointer, dir_pointer
|
180
|
+
|
181
|
+
rate = rate_pointer.read_int
|
182
|
+
|
183
|
+
rate_pointer.free
|
184
|
+
dir_pointer.free
|
185
|
+
|
186
|
+
error_code
|
187
|
+
end
|
188
|
+
rate
|
189
|
+
end
|
190
|
+
|
191
|
+
def sample_format=(sample_format)
|
192
|
+
ALSA::try_to "set sample format" do
|
193
|
+
ALSA::PCM::Native::hw_params_set_format self.device.handle, self.handle, ALSA::PCM::Native::Format.const_get(sample_format.to_s.upcase)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def sample_format
|
198
|
+
format = nil
|
199
|
+
FFI::MemoryPointer.new(:int) do |format_pointer|
|
200
|
+
ALSA::try_to "get sample format" do
|
201
|
+
ALSA::PCM::Native::hw_params_get_format self.handle, format_pointer
|
202
|
+
end
|
203
|
+
format = format_pointer.read_int
|
204
|
+
end
|
205
|
+
format
|
206
|
+
end
|
207
|
+
|
208
|
+
def channels
|
209
|
+
channels = nil
|
210
|
+
FFI::MemoryPointer.new(:int) do |channels_pointer|
|
211
|
+
ALSA::try_to "get channels" do
|
212
|
+
ALSA::PCM::Native::hw_params_get_channels self.handle, channels_pointer
|
213
|
+
end
|
214
|
+
channels = channels_pointer.read_int
|
215
|
+
end
|
216
|
+
channels
|
217
|
+
end
|
218
|
+
|
219
|
+
def buffer_size_for(frame_count)
|
220
|
+
ALSA::PCM::Native::format_size(self.sample_format, frame_count) * self.channels
|
221
|
+
end
|
222
|
+
|
223
|
+
def free
|
224
|
+
ALSA::try_to "unallocate hw_params" do
|
225
|
+
ALSA::PCM::Native::hw_params_free self.handle
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
module Native
|
234
|
+
extend FFI::Library
|
235
|
+
ffi_lib "libasound.so"
|
236
|
+
|
237
|
+
STREAM_CAPTURE = 1
|
238
|
+
BLOCK = 0
|
239
|
+
attach_function :open, :snd_pcm_open, [:pointer, :string, :int, :int], :int
|
240
|
+
attach_function :prepare, :snd_pcm_prepare, [ :pointer ], :int
|
241
|
+
attach_function :close, :snd_pcm_close, [:pointer], :int
|
242
|
+
|
243
|
+
attach_function :readi, :snd_pcm_readi, [ :pointer, :pointer, :ulong ], :long
|
244
|
+
|
245
|
+
attach_function :hw_params_malloc, :snd_pcm_hw_params_malloc, [:pointer], :int
|
246
|
+
attach_function :hw_params_free, :snd_pcm_hw_params_free, [:pointer], :int
|
247
|
+
|
248
|
+
attach_function :hw_params, :snd_pcm_hw_params, [ :pointer, :pointer ], :int
|
249
|
+
attach_function :hw_params_any, :snd_pcm_hw_params_any, [:pointer, :pointer], :int
|
250
|
+
attach_function :hw_params_current, :snd_pcm_hw_params_current, [ :pointer, :pointer ], :int
|
251
|
+
|
252
|
+
|
253
|
+
module Access
|
254
|
+
MMAP_INTERLEAVED = 0
|
255
|
+
MMAP_NONINTERLEAVED = 1
|
256
|
+
MMAP_COMPLEX = 2
|
257
|
+
RW_INTERLEAVED = 3
|
258
|
+
RW_NONINTERLEAVED = 4
|
259
|
+
end
|
260
|
+
|
261
|
+
attach_function :hw_params_set_access, :snd_pcm_hw_params_set_access, [ :pointer, :pointer, :int ], :int
|
262
|
+
|
263
|
+
module Format
|
264
|
+
S16_LE = 2
|
265
|
+
end
|
266
|
+
|
267
|
+
attach_function :hw_params_set_format, :snd_pcm_hw_params_set_format, [ :pointer, :pointer, :int ], :int
|
268
|
+
attach_function :hw_params_get_format, :snd_pcm_hw_params_get_format, [ :pointer, :pointer ], :int
|
269
|
+
attach_function :hw_params_get_rate, :snd_pcm_hw_params_get_rate, [ :pointer, :pointer, :pointer ], :int
|
270
|
+
attach_function :hw_params_set_rate_near, :snd_pcm_hw_params_set_rate_near, [ :pointer, :pointer, :pointer, :pointer ], :int
|
271
|
+
attach_function :hw_params_set_channels, :snd_pcm_hw_params_set_channels, [ :pointer, :pointer, :uint ], :int
|
272
|
+
attach_function :hw_params_get_channels, :snd_pcm_hw_params_get_format, [ :pointer, :pointer ], :int
|
273
|
+
attach_function :hw_params_set_periods, :snd_pcm_hw_params_set_periods, [ :pointer, :pointer, :uint, :int ], :int
|
274
|
+
|
275
|
+
attach_function :format_size, :snd_pcm_format_size, [ :int, :uint ], :int
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
data/lib/alsa_backup.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubygems'
|
6
|
+
rescue LoadError
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'activesupport'
|
10
|
+
require 'logger'
|
11
|
+
|
12
|
+
module AlsaBackup
|
13
|
+
VERSION = '0.0.1'
|
14
|
+
|
15
|
+
def self.recorder
|
16
|
+
@recorder ||= AlsaBackup::Recorder.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.config
|
20
|
+
yield self.recorder
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.logger
|
24
|
+
unless @logger
|
25
|
+
@logger = Logger.new(STDOUT)
|
26
|
+
@logger.level = Logger::INFO
|
27
|
+
end
|
28
|
+
|
29
|
+
@logger
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.logger=(logger); @logger = logger; end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
class Time
|
37
|
+
|
38
|
+
def floor(attribute, modulo)
|
39
|
+
actual = self.send(attribute)
|
40
|
+
self.change(attribute => actual - actual%modulo)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
require 'alsa_backup/recorder'
|
46
|
+
|
47
|
+
|
48
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module AlsaBackup
|
4
|
+
class CLI
|
5
|
+
def self.execute(stdout, *arguments)
|
6
|
+
options = {}
|
7
|
+
mandatory_options = %w()
|
8
|
+
|
9
|
+
OptionParser.new do |opts|
|
10
|
+
opts.banner = <<-BANNER.gsub(/^ /,'')
|
11
|
+
Alsa.Backup : continuous recording with alsa
|
12
|
+
|
13
|
+
Usage: #{File.basename($0)} [options]
|
14
|
+
|
15
|
+
Options are:
|
16
|
+
BANNER
|
17
|
+
opts.separator ""
|
18
|
+
opts.on("-f", "--file=FILE", String,
|
19
|
+
"Recording file") { |arg| options[:file] = arg }
|
20
|
+
opts.on("-l", "--length=LENGTH", String,
|
21
|
+
"Length in seconds") { |arg| options[:length] = arg }
|
22
|
+
opts.on("-d", "--directory=DIRECTORY", String,
|
23
|
+
"Base directory") { |arg| options[:directory] = arg }
|
24
|
+
opts.on("-c", "--config=CONFIG", String,
|
25
|
+
"Configuration file") { |arg| options[:config] = arg }
|
26
|
+
opts.on("-h", "--help",
|
27
|
+
"Show this help message.") { stdout.puts opts; exit }
|
28
|
+
opts.parse!(arguments)
|
29
|
+
|
30
|
+
if mandatory_options && mandatory_options.find { |option| options[option.to_sym].nil? }
|
31
|
+
stdout.puts opts; exit
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
load File.expand_path(options[:config]) if options[:config]
|
36
|
+
|
37
|
+
AlsaBackup.recorder.file = options[:file] if options[:file]
|
38
|
+
AlsaBackup.recorder.directory = options[:directory] if options[:directory]
|
39
|
+
|
40
|
+
length = options[:length].to_i if options[:length]
|
41
|
+
AlsaBackup.recorder.start(length)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'alsa'
|
2
|
+
require 'sndfile'
|
3
|
+
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
module AlsaBackup
|
7
|
+
class Recorder
|
8
|
+
|
9
|
+
def initialize(file = "record.wav")
|
10
|
+
@file = file
|
11
|
+
@directory = "."
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :file, :directory
|
15
|
+
|
16
|
+
def start(seconds_to_record = nil)
|
17
|
+
frames_to_record = format[:sample_rate] * seconds_to_record if seconds_to_record
|
18
|
+
|
19
|
+
# prepare sndfile
|
20
|
+
self.sndfile
|
21
|
+
|
22
|
+
ALSA::PCM::Capture.open("hw:0", self.format(:sample_format => :s16_le)) do |capture|
|
23
|
+
capture.read do |buffer, frame_count|
|
24
|
+
self.sndfile.write buffer, frame_count
|
25
|
+
if frames_to_record
|
26
|
+
(frames_to_record -= frame_count) > 0
|
27
|
+
else
|
28
|
+
true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
rescue Exception => e
|
33
|
+
AlsaBackup.logger.error(e)
|
34
|
+
raise e
|
35
|
+
ensure
|
36
|
+
@sndfile.close if @sndfile
|
37
|
+
end
|
38
|
+
|
39
|
+
def file
|
40
|
+
case @file
|
41
|
+
when Proc
|
42
|
+
@file.call
|
43
|
+
else
|
44
|
+
@file
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def target_file
|
49
|
+
File.join self.directory, self.file
|
50
|
+
end
|
51
|
+
|
52
|
+
def format(additional_parameters = {})
|
53
|
+
{:sample_rate => 44100, :channels => 2}.merge(additional_parameters)
|
54
|
+
end
|
55
|
+
|
56
|
+
def sndfile
|
57
|
+
target_file = self.target_file
|
58
|
+
raise "no recording file" unless target_file
|
59
|
+
|
60
|
+
unless @sndfile and @sndfile.path == target_file
|
61
|
+
@sndfile.close if @sndfile
|
62
|
+
AlsaBackup.logger.info "new file #{target_file}"
|
63
|
+
|
64
|
+
FileUtils.mkdir_p File.dirname(target_file)
|
65
|
+
@sndfile = Sndfile::File.new(target_file, "w", self.format(:format => "wav pcm_16"))
|
66
|
+
end
|
67
|
+
@sndfile
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
data/lib/sndfile.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
module Sndfile
|
5
|
+
|
6
|
+
class File
|
7
|
+
|
8
|
+
attr_reader :path
|
9
|
+
|
10
|
+
def self.open(path, mode, info)
|
11
|
+
file = self.new(path, mode, info)
|
12
|
+
|
13
|
+
begin
|
14
|
+
yield file
|
15
|
+
ensure
|
16
|
+
file.close
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(path, mode, info)
|
21
|
+
info = (Hash === info ? Info.new(info) : info)
|
22
|
+
@handle = Sndfile::Native::open path, File.native_mode(mode), info.to_native
|
23
|
+
if @handle.is_a?(FFI::NullPointer)
|
24
|
+
raise "Not able to open output file " + self.error
|
25
|
+
end
|
26
|
+
@path = path
|
27
|
+
end
|
28
|
+
|
29
|
+
def write(buffer, frame_count)
|
30
|
+
unless Sndfile::Native::write_int(@handle, buffer, frame_count) == frame_count
|
31
|
+
raise self.error
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def close
|
36
|
+
Sndfile::Native::close @handle
|
37
|
+
end
|
38
|
+
|
39
|
+
def error
|
40
|
+
Sndfile::Native::strerror @handle
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.native_mode(mode)
|
44
|
+
case mode
|
45
|
+
when "w": Sndfile::Native::MODE_WRITE
|
46
|
+
else
|
47
|
+
raise "Unknown mode: #{mode}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
class Info
|
54
|
+
|
55
|
+
attr_accessor :sample_rate, :channels, :format
|
56
|
+
|
57
|
+
def initialize(attributes = {})
|
58
|
+
update_attributes(attributes)
|
59
|
+
end
|
60
|
+
|
61
|
+
def update_attributes(attributes)
|
62
|
+
attributes.each_pair { |name, value| send("#{name}=", value) }
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_native
|
66
|
+
Sndfile::Native::Info.new.tap do |native|
|
67
|
+
native[:samplerate] = self.sample_rate
|
68
|
+
native[:channels] = self.channels
|
69
|
+
native[:format] = self.native_format
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def format=(format)
|
74
|
+
@format = Info.normalized_format(format)
|
75
|
+
end
|
76
|
+
|
77
|
+
def native_format
|
78
|
+
self.format.inject(0) do |native_format, format_part|
|
79
|
+
native_format | Sndfile::Native::Format.const_get(format_part.upcase)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.normalized_format(format)
|
84
|
+
Array(format).join(' ').downcase.scan(/[a-z0-9_]+/)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
module Native
|
90
|
+
extend FFI::Library
|
91
|
+
ffi_lib "libsndfile.so"
|
92
|
+
|
93
|
+
module Format
|
94
|
+
WAV = 0x010000
|
95
|
+
PCM_16 = 0x0002
|
96
|
+
PCM_24 = 0x0003
|
97
|
+
end
|
98
|
+
|
99
|
+
MODE_READ = 0x10
|
100
|
+
MODE_WRITE = 0x20
|
101
|
+
MODE_RDWR = 0x30
|
102
|
+
|
103
|
+
class Info < FFI::Struct
|
104
|
+
layout(
|
105
|
+
:frames, :int64,
|
106
|
+
:samplerate, :int,
|
107
|
+
:channels, :int,
|
108
|
+
:format, :int,
|
109
|
+
:sections, :int,
|
110
|
+
:seekable, :int
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
attach_function :open, :sf_open, [ :string, :int, :pointer ], :pointer
|
115
|
+
attach_function :close, :sf_close, [ :pointer ], :int
|
116
|
+
|
117
|
+
attach_function :write_int, :sf_write_int, [ :pointer, :pointer, :int ], :int
|
118
|
+
|
119
|
+
attach_function :strerror, :sf_strerror, [ :pointer ], :string
|
120
|
+
end
|
121
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/alsa_backup.rb'}"
|
9
|
+
puts "Loading alsa_backup gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
require 'alsa_backup/cli'
|
4
|
+
|
5
|
+
describe AlsaBackup::CLI, "execute" do
|
6
|
+
before(:each) do
|
7
|
+
@stdout_io = StringIO.new
|
8
|
+
@file = test_file
|
9
|
+
|
10
|
+
@recorder = AlsaBackup::Recorder.new(@file)
|
11
|
+
@recorder.stub!(:start)
|
12
|
+
AlsaBackup.stub!(:recorder).and_return(@recorder)
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute_cli(options = {})
|
16
|
+
options = { :file => @file, :length => 2 }.update(options)
|
17
|
+
arguments = options.collect do |key,value|
|
18
|
+
"--#{key}=#{value}" if value
|
19
|
+
end.compact
|
20
|
+
|
21
|
+
AlsaBackup::CLI.execute(@stdout_io, *arguments)
|
22
|
+
@stdout_io.rewind
|
23
|
+
@stdout = @stdout_io.read
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should use AlsaBackup.recorder" do
|
27
|
+
AlsaBackup.should_receive(:recorder).and_return(@recorder)
|
28
|
+
execute_cli
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should set the record file with specified one" do
|
32
|
+
@recorder.should_receive(:file=).with(file = "dummy")
|
33
|
+
execute_cli :file => file
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should start the AlsaBackup.recorder" do
|
37
|
+
@recorder.should_receive(:start)
|
38
|
+
execute_cli
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should start the recorder with specified length" do
|
42
|
+
@recorder.should_receive(:start).with(length = 60)
|
43
|
+
execute_cli :length => length
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should start the record without length if not specified" do
|
47
|
+
@recorder.should_receive(:start).with(nil)
|
48
|
+
execute_cli :length => nil
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should execute specified config file" do
|
52
|
+
execute_cli :config => fixture_file('config_test.rb'), :file => nil
|
53
|
+
AlsaBackup.recorder.file.should == "config_test_ok"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should override config file values with command line arguments" do
|
57
|
+
argument_file = "dummy"
|
58
|
+
execute_cli :config => fixture_file('config_test.rb'), :file => argument_file
|
59
|
+
AlsaBackup.recorder.file.should == argument_file
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
require 'alsa_backup/recorder'
|
4
|
+
|
5
|
+
describe AlsaBackup::Recorder do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@file = test_file
|
9
|
+
@recorder = AlsaBackup::Recorder.new(@file)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should not raise an error on start" do
|
13
|
+
lambda { @recorder.start(2) }.should_not raise_error
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "file" do
|
17
|
+
|
18
|
+
it "should accept file as string" do
|
19
|
+
@recorder.file = file_name = "dummy"
|
20
|
+
@recorder.file.should == file_name
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should accept file as Proc" do
|
24
|
+
file_name = "dummy"
|
25
|
+
@recorder.file = Proc.new { file_name }
|
26
|
+
@recorder.file.should == file_name
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
require 'sndfile'
|
4
|
+
|
5
|
+
describe Sndfile::Info do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@info = Sndfile::Info.new
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "normalized_format" do
|
12
|
+
|
13
|
+
it "should transform 'wav pcm_16' into [wav, pcm_16]" do
|
14
|
+
Sndfile::Info.normalized_format('wav pcm_16').should == %w{wav pcm_16}
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "format" do
|
20
|
+
|
21
|
+
it "should be normalized when set" do
|
22
|
+
@info.format = 'wav pcm_16'
|
23
|
+
@info.format.should == %w{wav pcm_16}
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "native_format" do
|
29
|
+
|
30
|
+
it "should return (Format::WAV|Format::PCM_16) for 'wav pcm_16'" do
|
31
|
+
@info.format = 'wav pcm_16'
|
32
|
+
@info.native_format.should == Sndfile::Native::Format::WAV | Sndfile::Native::Format::PCM_16
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
5
|
+
gem 'rspec'
|
6
|
+
require 'spec'
|
7
|
+
end
|
8
|
+
|
9
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
10
|
+
require 'alsa_backup'
|
11
|
+
|
12
|
+
def test_file(name = 'test.wav')
|
13
|
+
File.dirname(__FILE__) + '/../tmp/' + name
|
14
|
+
end
|
15
|
+
|
16
|
+
def fixture_file(name)
|
17
|
+
File.dirname(__FILE__) + '/fixtures/' + name
|
18
|
+
end
|
19
|
+
|
20
|
+
ALSA::logger = AlsaBackup.logger =
|
21
|
+
Logger.new(File.dirname(__FILE__) + '/../log/test.log')
|
data/tasks/rspec.rake
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
5
|
+
require 'spec'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'spec/rake/spectask'
|
9
|
+
rescue LoadError
|
10
|
+
puts <<-EOS
|
11
|
+
To use rspec for testing you must install rspec gem:
|
12
|
+
gem install rspec
|
13
|
+
EOS
|
14
|
+
exit(0)
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Run the specs under spec/models"
|
18
|
+
Spec::Rake::SpecTask.new do |t|
|
19
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
20
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: albanpeignier-alsa-backup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alban Peignier
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-05-18 00:00:00 -07:00
|
13
|
+
default_executable: alsa.backup
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: ffi
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.3.5
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: newgem
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.4.1
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: hoe
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.8.0
|
44
|
+
version:
|
45
|
+
description: ALSA client to perform continuous recording
|
46
|
+
email:
|
47
|
+
- alban.peignier@free.fr
|
48
|
+
executables:
|
49
|
+
- alsa.backup
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- History.txt
|
54
|
+
- Manifest.txt
|
55
|
+
- PostInstall.txt
|
56
|
+
- README.rdoc
|
57
|
+
files:
|
58
|
+
- .autotest
|
59
|
+
- History.txt
|
60
|
+
- Manifest.txt
|
61
|
+
- PostInstall.txt
|
62
|
+
- README.rdoc
|
63
|
+
- Rakefile
|
64
|
+
- alsa-backup.gemspec
|
65
|
+
- bin/alsa.backup
|
66
|
+
- config.sample
|
67
|
+
- lib/alsa.rb
|
68
|
+
- lib/alsa_backup.rb
|
69
|
+
- lib/alsa_backup/cli.rb
|
70
|
+
- lib/alsa_backup/recorder.rb
|
71
|
+
- lib/sndfile.rb
|
72
|
+
- script/console
|
73
|
+
- script/destroy
|
74
|
+
- script/generate
|
75
|
+
- spec/alsa/pcm_spec.rb
|
76
|
+
- spec/alsa_backup/cli_spec.rb
|
77
|
+
- spec/alsa_backup/recorder_spec.rb
|
78
|
+
- spec/alsa_backup_spec.rb
|
79
|
+
- spec/fixtures/config_test.rb
|
80
|
+
- spec/sndfile/info_spec.rb
|
81
|
+
- spec/spec.opts
|
82
|
+
- spec/spec_helper.rb
|
83
|
+
- tasks/rspec.rake
|
84
|
+
has_rdoc: true
|
85
|
+
homepage: http://github.com/alban.peignier/alsa-backup
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options:
|
88
|
+
- --main
|
89
|
+
- README.rdoc
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: "0"
|
97
|
+
version:
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: "0"
|
103
|
+
version:
|
104
|
+
requirements: []
|
105
|
+
|
106
|
+
rubyforge_project: alsa-backup
|
107
|
+
rubygems_version: 1.2.0
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: ALSA client to perform continuous recording
|
111
|
+
test_files: []
|
112
|
+
|