ruby-sox 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b3d0a858629f10877dee7bc1be0f707777f3215d
4
+ data.tar.gz: bae090b2b7124e2a37f15b1c9001bfa6ed79fced
5
+ SHA512:
6
+ metadata.gz: a532d4ab550278235dc139d9aa297f7494ba49b06d051a0c1e4e709cfbf78e837915f8f128b5482d6f6bbc83425020095b217f50fec6dcaf2e372c41692192e5
7
+ data.tar.gz: c428ddd99eb71e1b157d79b10c301f7a2dd320e4ed8fe332e924d0ec5e19cd6744c13ecec1142b9dd703e5cfd291d82433d01b057ba2f4f22f9f03f4c9d69bcf
data/LICENSE.txt ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2013 TMX Credit, author Potapov Sergey
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ The audio files are distributed under Creative Commons Attribution 3.0
23
+ Unported License.
data/README.markdown ADDED
@@ -0,0 +1,114 @@
1
+ # Ruby SoX
2
+
3
+ [![Build Status](https://travis-ci.org/TMXCredit/ruby-sox.png?branch=master)](https://travis-ci.org/TMXCredit/ruby-sox)
4
+
5
+ A Ruby wrapper for the `sox` command line tool to process sound.
6
+
7
+
8
+ ## Dependencies
9
+
10
+ * SoX
11
+ * Bash (for process substitution combiner strategy)
12
+ * Chromaprint (only to run tests)
13
+
14
+ ### Debian / Ubuntu
15
+
16
+ ```bash
17
+ apt-get install libsox-fmt-all sox libchromaprint-dev
18
+ ```
19
+
20
+ ### Mac
21
+
22
+ ```bash
23
+ # One of the following
24
+ # Notes:
25
+ # * chromaprint is not available in MacPorts as of this writing
26
+ # * flac must be installed before sox so it will link during compilation
27
+ sudo port install flac sox && brew install chromaprint
28
+ brew install flac sox chromaprint
29
+ ```
30
+
31
+ # Usage
32
+
33
+ Remember that it's a wrapper for `sox` to provide Ruby API.
34
+ It's assumed that you know the basics of sound computing theory and how
35
+ the `sox` tool works. Otherwise, `man sox` is your good friend.
36
+
37
+ ## Sox::Cmd
38
+
39
+ Allows you to do everything that the `sox` command does.
40
+
41
+ Mix 3 files into one (ruby-sox assumes that input files have same rate and number of channels):
42
+
43
+ ```ruby
44
+ # Build command
45
+ sox = Sox::Cmd.new(:combine => :mix)
46
+ .add_input("guitar1.flac")
47
+ .add_input("guitar2.flac")
48
+ .add_input("drums.flac")
49
+ .set_output("hell_rock-n-roll.mp3")
50
+ .set_effects(:rate => 44100, :channels => 2)
51
+
52
+ # Execute command
53
+ sox.run
54
+ ```
55
+
56
+ ## Sox::Combiner
57
+
58
+ Sox::Combiner combines files even if they have different rates or numbers
59
+ of channels. Under the hood, it converts the input files into temporary files
60
+ to have the same rate and number of channels, and then combines them.
61
+
62
+ Concatenate:
63
+
64
+ ```ruby
65
+ combiner = Sox::Combiner.new('in1.mp3', 'in2.ogg', 'in3.wav', :combine => :concatenate)
66
+ combiner.write('out.mp3')
67
+ ```
68
+
69
+ Mix 3 files into an MP3 with 2 channels and a rate of 1600:
70
+
71
+ ```ruby
72
+ combiner = Sox::Combiner.new('in1.mp3', 'in2.ogg', 'in3.wav', :combine => :mix, :rate => 1600, :channels => 2)
73
+ combiner.write('out.mp3')
74
+ ```
75
+
76
+ ## Run specs
77
+
78
+ ```bash
79
+ rake spec
80
+ ```
81
+
82
+ ## Credits
83
+
84
+ * [Sergey Potapov](https://github.com/greyblake)
85
+
86
+ ## License
87
+
88
+ Copyright (c) 2013 TMX Credit, author Potapov Sergey
89
+
90
+ Permission is hereby granted, free of charge, to any person obtaining
91
+ a copy of this software and associated documentation files (the
92
+ "Software"), to deal in the Software without restriction, including
93
+ without limitation the rights to use, copy, modify, merge, publish,
94
+ distribute, sublicense, and/or sell copies of the Software, and to
95
+ permit persons to whom the Software is furnished to do so, subject to
96
+ the following conditions:
97
+
98
+ The above copyright notice and this permission notice shall be
99
+ included in all copies or substantial portions of the Software.
100
+
101
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
102
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
103
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
104
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
105
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
106
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
107
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
108
+
109
+ The audio files are distributed under [Creative Commons Attribution 3.0
110
+ Unported License](http://creativecommons.org/licenses/by/3.0/legalcode).
111
+
112
+ ## Copyright
113
+
114
+ Copyright (c) 2013 TMX Credit.
data/lib/ruby-sox.rb ADDED
@@ -0,0 +1,2 @@
1
+ require File.expand_path('../sox', __FILE__)
2
+
data/lib/sox/cmd.rb ADDED
@@ -0,0 +1,85 @@
1
+ module Sox
2
+ # Process audio files using the +sox+ shell command.
3
+ #
4
+ # @example
5
+ # # Mix 3 files into one
6
+ # sox = Sox::Cmd.new(:combine => :mix)
7
+ # sox.add_input("guitar1.flac")
8
+ # sox.add_input("guitar2.flac")
9
+ # sox.add_input("drums.flac")
10
+ # sox.set_output("hell_rock-n-roll.mp3")
11
+ # sox.set_effects(:rate => 44100, :channels => 2)
12
+ # sox.run
13
+ class Cmd
14
+ include Sox::Shell
15
+
16
+ attr_reader :options, :inputs, :output, :effects
17
+
18
+ # @param options [Hash] global options for sox command
19
+ def initialize(options = {})
20
+ @options = options
21
+ @inputs = []
22
+ @effects = {}
23
+ end
24
+
25
+ # Add input file with its options.
26
+ #
27
+ # @param file_path [String] path to file
28
+ # @param input_options [Hash] options for input files, see +man sox+
29
+ #
30
+ # @return [Sox::Cmd] self
31
+ def add_input(file_path, input_options = {})
32
+ @inputs << Sox::File.new(file_path, input_options)
33
+ self
34
+ end
35
+
36
+ # Set output file and its options.
37
+ #
38
+ # @param file_path [String] ouput file path
39
+ # @param output_options [Hash] options for output file, see +man sox+
40
+ #
41
+ # @return [Sox::Cmd] self
42
+ def set_output(file_path, output_options = {})
43
+ @output = Sox::File.new(file_path, output_options)
44
+ self
45
+ end
46
+
47
+ # Set effects on the output file. See +man sox+ section +EFFECTS+.
48
+ # It receives the effect name as a hash key and the effect arguments as
49
+ # hash values which can be a string or an array of strings. If an effect
50
+ # has no arguments just pass +true+ as the value.
51
+ #
52
+ # @example
53
+ # # Normalize and use 2 channels for output
54
+ # sox_cmd.set_effects(:channels => 2, :norm => true)
55
+ #
56
+ # @param effects [Hash{Symbol, String => Symbol, String, Array<String>}]
57
+ #
58
+ # @return [Sox::Cmd] self
59
+ def set_effects(effects)
60
+ @effects = effects
61
+ self
62
+ end
63
+
64
+ # Set global options. See +man sox+ section +Global Options+.
65
+ #
66
+ # @param options [Hash] global options for +sox+ command
67
+ #
68
+ # @return [Sox::Cmd] self
69
+ def set_options(options)
70
+ @options = options
71
+ self
72
+ end
73
+
74
+ # Run `sox` command. Raise {Sox::Error} on fail.
75
+ #
76
+ # @return [Boolean] true in case of success
77
+ def run
78
+ raise(Sox::Error, "Output is missing, specify it with `set_output`") unless @output
79
+ raise(Sox::Error, "Inputs are missing, specify them with `add_input`") if @inputs.empty?
80
+
81
+ cmd = CommandBuilder.new(@inputs, @output, @options, @effects).build
82
+ sh(cmd)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,37 @@
1
+ module Sox
2
+ # Common parent class for combiner strategies.
3
+ class Combiner::BaseStrategy
4
+ include Sox::Shell
5
+
6
+ # @param input_files [Array<String>] input files
7
+ # @param options [Hash] see {Sox::Combiner#initialize}
8
+ def initialize(input_files, options)
9
+ @input_files = input_files
10
+ @options = options
11
+ end
12
+
13
+ # Run the command, and save output in the output file.
14
+ #
15
+ # @param output_file [String]
16
+ #
17
+ # @return [void]
18
+ def write(output_file)
19
+ raise NotImplementedError, __method__
20
+ end
21
+
22
+
23
+ # Build effects which will be applied on final output.
24
+ #
25
+ # @return [Hash]
26
+ def output_effects
27
+ {:norm => @options[:norm]}
28
+ end
29
+
30
+ # Build global options for +sox+ command.
31
+ #
32
+ # @return [Hash]
33
+ def output_options
34
+ {:combine => @options[:combine]}
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,78 @@
1
+ module Sox
2
+ # Combines files using process substitution to build and use mediate files.
3
+ # Process substitution is not supported by the standard shell, so we need
4
+ # to pass the final command to bash via shell.
5
+ #
6
+ # Read more about process substitution:
7
+ # http://en.wikipedia.org/wiki/Process_substitution
8
+ class Combiner::ProcessSubstitutionStrategy < Combiner::BaseStrategy
9
+ # Type of mediate files:
10
+ MEDIATE_TYPE = :sox
11
+
12
+ # Number of bits for mediate files:
13
+ MEDIATE_BITS = 32
14
+
15
+ # Encoding of mediate files:
16
+ MEDIATE_ENCODING = :signed
17
+
18
+ # Pseudo file which makes the `sox` command write to output to stdout:
19
+ SOX_PIPE = '-p'
20
+
21
+ # :nodoc:
22
+ def write(output_file)
23
+ inputs = @input_files.map { |file| build_input_file(file) }
24
+ output = Sox::File.new(output_file)
25
+
26
+ cmd = CommandBuilder.new(inputs, output, output_options, output_effects).build
27
+ bash(cmd)
28
+ end
29
+
30
+
31
+ # Build the input file which can be used in the command builder to get
32
+ # the final output.
33
+ #
34
+ # @param file_path [String] path to input file
35
+ #
36
+ # @return [Sox::File]
37
+ def build_input_file(file_path)
38
+ Sox::File.new(build_converted_input(file_path),
39
+ :type => MEDIATE_TYPE,
40
+ :encoding => MEDIATE_ENCODING,
41
+ :bits => MEDIATE_BITS,
42
+ :channels => @options[:channels],
43
+ :rate => @options[:rate]
44
+ ).tap { |file| file.escaped = true }
45
+ end
46
+ private :build_input_file
47
+
48
+ # Build the shell statement which can be used as the input file with
49
+ # the needed rate and number of channels.
50
+ #
51
+ # @param input_file [String]
52
+ #
53
+ # @return [String] shell statement which can be used as input file
54
+ #
55
+ # @example
56
+ # build_converted_input("in.mp3")
57
+ # # => "<(sox in.mp3 --encoding sox --bits 32 -p rate 22100 channels 1)"
58
+ def build_converted_input(input_file)
59
+ input = Sox::File.new(input_file)
60
+ output = Sox::File.new(SOX_PIPE, :encoding => MEDIATE_ENCODING, :bits => MEDIATE_BITS)
61
+ effects = {:channels => @options[:channels], :rate => @options[:rate]}
62
+ command = CommandBuilder.new([input], output, {}, effects).build
63
+ process_substitution_wrap(command)
64
+ end
65
+ private :build_converted_input
66
+
67
+ # Wrap shell command to make its output be used in process substitution.
68
+ # See http://en.wikipedia.org/wiki/Process_substitution for more info.
69
+ #
70
+ # @param command [String] shell command
71
+ #
72
+ # @return [String]
73
+ def process_substitution_wrap(command)
74
+ "<(#{command})"
75
+ end
76
+ private :process_substitution_wrap
77
+ end
78
+ end
@@ -0,0 +1,78 @@
1
+ module Sox
2
+ # Combines files using temporary files as mediate files.
3
+ class Combiner::TmpFileStrategy < Combiner::BaseStrategy
4
+ # Type of temporary mediate files:
5
+ MEDIATE_TYPE = :raw
6
+
7
+ # Number of bits for temporary mediate files:
8
+ MEDIATE_BITS = 16
9
+
10
+ # Encoding of temporary mediate files:
11
+ MEDIATE_ENCODING = :signed
12
+
13
+
14
+ # :nodoc:
15
+ def write(output_file)
16
+ tmp_files = []
17
+
18
+ @input_files.each do |input_file|
19
+ tmp_output_file = gen_tmp_filename
20
+ tmp_files << tmp_output_file
21
+
22
+ cmd = build_convert_command(input_file, tmp_output_file)
23
+ sh(cmd)
24
+ end
25
+
26
+ cmd = build_output_command(tmp_files, output_file)
27
+ sh(cmd)
28
+ ensure
29
+ # Remove temporary files
30
+ tmp_files.each { |file| FileUtils.rm(file) if ::File.exists?(file) }
31
+ end
32
+
33
+ # Build +sox+ command to get final output.
34
+ #
35
+ # @param input_files [Array<String>]
36
+ # @param output_file [String]
37
+ #
38
+ # @return [String] sox command
39
+ def build_output_command(input_files, output_file)
40
+ inputs = input_files.map do |path|
41
+ Sox::File.new(path,
42
+ :type => MEDIATE_TYPE,
43
+ :encoding => MEDIATE_ENCODING,
44
+ :bits => MEDIATE_BITS,
45
+ :channels => @options[:channels],
46
+ :rate => @options[:rate])
47
+ end
48
+
49
+ output = Sox::File.new(output_file)
50
+
51
+ builder = CommandBuilder.new(inputs, output, output_options, output_effects)
52
+ builder.build
53
+ end
54
+ private :build_output_command
55
+
56
+ # Build shell command which converts input file into temporary file with
57
+ # desired rate and channels.
58
+ #
59
+ # @param input_file [String] input file
60
+ # @param output_file [String] converted output with desired characteristics
61
+ #
62
+ # @return [String] shell command
63
+ def build_convert_command(input_file, output_file)
64
+ builder = CommandBuilder.new([Sox::File.new(input_file)], Sox::File.new(output_file))
65
+ builder.effects = {:channels => @options[:channels], :rate => @options[:rate]}
66
+ builder.build
67
+ end
68
+ private :build_convert_command
69
+
70
+ # Generate path to temporary file with unique name.
71
+ #
72
+ # @return [String] path to temporary file
73
+ def gen_tmp_filename
74
+ Dir::Tmpname.make_tmpname ['/tmp/ruby-sox', ".#{MEDIATE_TYPE}"], nil
75
+ end
76
+ private :gen_tmp_filename
77
+ end
78
+ end
@@ -0,0 +1,73 @@
1
+ module Sox
2
+ # Combines input files. Technically it calls +sox+ with +--combine+ option,
3
+ # but allows you not to care about the rates and numbers of channels in the
4
+ # input files. It converts them to the same rates/channels using temporary
5
+ # mediate files.
6
+ #
7
+ # @example
8
+ # # Concatenate
9
+ # combiner = Sox::Combiner.new('in1.mp3', 'in2.ogg', 'in3.wav', :combine => :concatenate)
10
+ # combiner.write('out.mp3')
11
+ class Combiner
12
+
13
+ autoload :BaseStrategy , 'sox/combiner/base_strategy'
14
+ autoload :TmpFileStrategy , 'sox/combiner/tmp_file_strategy'
15
+ autoload :ProcessSubstitutionStrategy, 'sox/combiner/process_substitution_strategy'
16
+
17
+ # Default options
18
+ DEFAULT_OPTIONS = {
19
+ # Method to be used for combining sounds. See --combine of sox tool.
20
+ :combine => :concatenate,
21
+
22
+ # Number of channels in the output file.
23
+ :channels => 1,
24
+
25
+ # Rate(samples per seconds) of the output file.
26
+ :rate => 22050,
27
+
28
+ # Apply norm effect on output.
29
+ :norm => false,
30
+
31
+ # Strategy to convert input files into files with the same rates
32
+ # and channels.
33
+ :strategy => :process_substitution
34
+ }
35
+
36
+ # Mapping of strategy names and their implementations
37
+ STRATEGIES = {
38
+ :tmp_file => TmpFileStrategy,
39
+ :process_substitution => ProcessSubstitutionStrategy
40
+ }
41
+
42
+
43
+ # @param input_files [Array<String>] input files
44
+ # @param options [Hash]
45
+ #
46
+ # @option options :combine [Symbol] value for +--combine+ sox option.
47
+ # Use underscore instead of hyphen, e.g. :mix_power.
48
+ # @option options :channels [Integer] number of channels in output file.
49
+ # @option options :rate [Integer] rate of output file
50
+ # @option options :norm [Boolean] apply +norm+ effect on output.
51
+ # @option options :strategy [Symbol] strategy to treat temporary files,
52
+ # default is :process_substitution which reduces disk IO.
53
+ def initialize(input_files, options = {})
54
+ raise(ArgumentError, "Input files are missing") if input_files.empty?
55
+
56
+ opts = DEFAULT_OPTIONS.merge(options)
57
+ strategy_name = opts.delete(:strategy)
58
+ strategy_class = STRATEGIES[strategy_name]
59
+ raise(ArgumentError, "Unknown strategy #{strategy_name.inspect}") unless strategy_class
60
+
61
+ @strategy = strategy_class.new(input_files, opts)
62
+ end
63
+
64
+ # Run +sox+ command and write output to file.
65
+ #
66
+ # @param output_file [String] path of output file
67
+ #
68
+ # @return [void]
69
+ def write(output_file)
70
+ @strategy.write(output_file)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,98 @@
1
+ module Sox
2
+ # Builds the +sox+ shell command from input files, an output file, options
3
+ # and effects.
4
+ #
5
+ # @example
6
+ # builder = Sox::CommandBuilder.new(['in1.mp3', 'in2.ogg'], 'out.wav',
7
+ # {:combine => :mix},
8
+ # {:rate => 44100, :channels => 2}
9
+ # )
10
+ # builder.build # => "sox --combine mix in1.mp3 in2.ogg out.wav rate 44100 channels 2"
11
+ class CommandBuilder
12
+ attr_accessor :input_files, :output_file, :options, :effects
13
+
14
+ # @param input_files [Array<Sox::File>]
15
+ # @param output_file [Sox::File]
16
+ # @param options [Hash{Symbol => Symbol}]
17
+ # @param effects [Hash{Symbol => Symbol}]
18
+ def initialize(input_files, output_file, options = {}, effects = {})
19
+ @input_files = input_files
20
+ @output_file = output_file
21
+ @options = options
22
+ @effects = effects
23
+ end
24
+
25
+ # Build shell command with all arguments and options.
26
+ #
27
+ # @return [String]
28
+ def build
29
+ [ Sox::SOX_COMMAND,
30
+ build_options(@options),
31
+ build_input_files,
32
+ build_file(@output_file),
33
+ build_effects
34
+ ].flatten.join(' ')
35
+ end
36
+
37
+
38
+ # Build input files with their options.
39
+ #
40
+ # @return [Array<String>]
41
+ def build_input_files
42
+ @input_files.map { |file| build_file(file) }
43
+ end
44
+ private :build_input_files
45
+
46
+ # Build part of SoX command which represents file(input or output).
47
+ #
48
+ # @param file [Sox::File] file
49
+ #
50
+ # @return [String]
51
+ def build_file(file)
52
+ opts = build_options(file.options)
53
+ file_path = file.escaped? ? file.path : Shellwords.escape(file.path)
54
+ [opts, file_path]
55
+ end
56
+ private :build_file
57
+
58
+ # Build options with their values (if present) to be used in shell command.
59
+ #
60
+ # @param options [Hash] options
61
+ #
62
+ # @return [Array<String>] options to be concatenated into string
63
+ def build_options(options)
64
+ options.inject([]) do |result, (opt, val)|
65
+ if val
66
+ result << "--#{shellify_opt(opt)}"
67
+ result << shellify_opt(val) if val != true
68
+ end
69
+ result
70
+ end
71
+ end
72
+ private :build_options
73
+
74
+ # Build effects with their arguments (if present) to be used in shell command.
75
+ #
76
+ # @return [Array<String>] effects to be concatenated into string
77
+ def build_effects
78
+ @effects.inject([]) do |result, (effect, val)|
79
+ if val
80
+ result << effect
81
+ result << val.to_s if val != true
82
+ end
83
+ result
84
+ end
85
+ end
86
+ private :build_effects
87
+
88
+ # Convert option or its value to shell style, separating words with "-".
89
+ #
90
+ # @param value [Symbol, String] option or value
91
+ #
92
+ # @return [String] shellified option
93
+ def shellify_opt(value)
94
+ value.to_s.gsub('_', '-')
95
+ end
96
+ private :shellify_opt
97
+ end
98
+ end
data/lib/sox/file.rb ADDED
@@ -0,0 +1,29 @@
1
+ module Sox
2
+ # Represents input or output file with its options for the `sox` command.
3
+ class File
4
+ # Path to file or whatever.
5
+ attr_reader :path
6
+
7
+ # File options which will be placed right before it.
8
+ attr_reader :options
9
+
10
+ # True if path doesn't need to be escaped.
11
+ attr_accessor :escaped
12
+
13
+
14
+ # @param path [String] path to file
15
+ # @param options [Hash{Symbol => Symbol,String,Numeric}] file options
16
+ def initialize(path, options = {})
17
+ @path = path
18
+ @options = options
19
+ @escaped = false
20
+ end
21
+
22
+ # Does the path need to be escaped?
23
+ #
24
+ # @return [Boolean]
25
+ def escaped?
26
+ @escaped
27
+ end
28
+ end
29
+ end
data/lib/sox/shell.rb ADDED
@@ -0,0 +1,45 @@
1
+ module Sox
2
+ # Provides methods to run the command in the +/bin/sh+ and +/bin/bash+
3
+ # interpreters. Ruby's `system` method runs +/bin/sh+ which doesn't support
4
+ # the process substitution feature. So sometimes we need to use bash with
5
+ # process substitution in order to avoid disk IO operations.
6
+ #
7
+ # Also this module takes care of error handling and raises {Sox::Error} when
8
+ # a failure message is returned by the shell command.
9
+ #
10
+ # See http://en.wikipedia.org/wiki/Process_substitution
11
+ module Shell
12
+ # Path to the +bash+ interpreter:
13
+ BASH_PATH = '/bin/bash'
14
+
15
+ # Run a shell command.
16
+ #
17
+ # @param command [String] shell command to execute
18
+ #
19
+ # @return [Boolean] true in case of success
20
+ def sh(command)
21
+ _, _, err_io, thread = Open3.popen3(command)
22
+ thread.join
23
+
24
+ process_status = thread.value
25
+ if process_status.success?
26
+ true
27
+ else
28
+ raise Error, err_io.read
29
+ end
30
+ rescue Errno::ENOENT => err
31
+ msg = "#{err.message}. Do you have `#{SOX_COMMAND}' installed?"
32
+ raise Error, msg
33
+ end
34
+
35
+ # Run bash command.
36
+ #
37
+ # @param command [String] bash command to execute
38
+ #
39
+ # @return [Boolean] true in case of success
40
+ def bash(command)
41
+ bash_command = "#{BASH_PATH} -c #{Shellwords.escape(command)}"
42
+ sh(bash_command)
43
+ end
44
+ end
45
+ end
data/lib/sox.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'shellwords'
2
+ require 'open3'
3
+ require 'tempfile'
4
+
5
+ # Wrapper around the +sox+ command line tool.
6
+ module Sox
7
+ # Basic SoX error:
8
+ class Error < StandardError
9
+ end
10
+
11
+ # The SoX command:
12
+ SOX_COMMAND = 'sox'.freeze
13
+ end
14
+
15
+ require 'sox/file'
16
+ require 'sox/shell'
17
+ require 'sox/command_builder'
18
+ require 'sox/cmd'
19
+ require 'sox/combiner'
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-sox
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Potapov Sergey
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: jeweler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 1.8.7
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 1.8.7
41
+ - !ruby/object:Gem::Dependency
42
+ name: yard
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard-rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: metric_fu
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Wrapper around sox sound tool
84
+ email: blake131313@gmail.com
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files:
88
+ - LICENSE.txt
89
+ - README.markdown
90
+ files:
91
+ - LICENSE.txt
92
+ - README.markdown
93
+ - lib/ruby-sox.rb
94
+ - lib/sox.rb
95
+ - lib/sox/cmd.rb
96
+ - lib/sox/combiner.rb
97
+ - lib/sox/combiner/base_strategy.rb
98
+ - lib/sox/combiner/process_substitution_strategy.rb
99
+ - lib/sox/combiner/tmp_file_strategy.rb
100
+ - lib/sox/command_builder.rb
101
+ - lib/sox/file.rb
102
+ - lib/sox/shell.rb
103
+ homepage: http://github.com/greyblake/ruby-sox
104
+ licenses:
105
+ - MIT
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 2.0.3
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: Wrapper around sox sound tool
127
+ test_files: []
128
+ has_rdoc: