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