chaussettes 1.0.0
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +120 -0
- data/Rakefile +7 -0
- data/examples/drum.rb +22 -0
- data/examples/ducking.rb +65 -0
- data/examples/synth.rb +22 -0
- data/lib/chaussettes.rb +2 -0
- data/lib/chaussettes/clip.rb +119 -0
- data/lib/chaussettes/common_options.rb +46 -0
- data/lib/chaussettes/effect/fade.rb +31 -0
- data/lib/chaussettes/effect/gain.rb +27 -0
- data/lib/chaussettes/effect/synth.rb +130 -0
- data/lib/chaussettes/effect/vol.rb +18 -0
- data/lib/chaussettes/effect_chain.rb +67 -0
- data/lib/chaussettes/info.rb +57 -0
- data/lib/chaussettes/input.rb +50 -0
- data/lib/chaussettes/output.rb +53 -0
- data/lib/chaussettes/tool.rb +27 -0
- data/lib/chaussettes/version.rb +3 -0
- data/test/chaussettes/clip_test.rb +78 -0
- data/test/chaussettes/input_test.rb +61 -0
- data/test/chaussettes/output_test.rb +61 -0
- data/test/chaussettes/tool_test.rb +27 -0
- metadata +99 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 09fa2339e4fa515bb12f28671bb4198871087411
|
4
|
+
data.tar.gz: bc3d65c38875e6a6c340aabe21fc1103e790e0f3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d1afb30a879e137ccfd6a92815e38f4082238da02aa98fb17f53f255abd6edb918f3ac76f29513ed6c366da33d63e352375fe0f7f98bbbc811e364072e200fc2
|
7
|
+
data.tar.gz: 0c87fb7238943af3ace082520b8957c12e29b338575c5484ea2c73cb8a5807188989c648d08cddebed35111bca5247e28b79d443c265951901d4461e699c35fa
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2016 Jamis Buck
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# Chaussettes
|
2
|
+
|
3
|
+
Chaussettes is a wrapper around the command-line `sox` utility. It enables you
|
4
|
+
to manipulate audio by programmatically constructing a sox operation, including
|
5
|
+
multiple effect chains and various supported input and output streams.
|
6
|
+
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Simply gem install it.
|
11
|
+
|
12
|
+
$ gem install chaussettes
|
13
|
+
|
14
|
+
It has no other dependencies, although it won't work
|
15
|
+
very well if you don't also install `sox`, which you can grab here: http://sox.sourceforge.net/.
|
16
|
+
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
To find out information about an existing clip, you can use the
|
21
|
+
`Chaussettes::Info` class:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
require 'chaussettes'
|
25
|
+
|
26
|
+
info = Chaussettes::Info.new('hello.mp3')
|
27
|
+
p info.duration # 192.3 (seconds)
|
28
|
+
```
|
29
|
+
|
30
|
+
To programmatically construct a sox command-line, use the `Chaussettes::Clip`
|
31
|
+
class:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
require 'chaussettes'
|
35
|
+
|
36
|
+
clip = Chaussettes::Clip.new do |c|
|
37
|
+
c.mix
|
38
|
+
c.in 'music-track.mp3'
|
39
|
+
c.in 'narration.wav'
|
40
|
+
c.out 'soundtrack.wav'
|
41
|
+
end
|
42
|
+
|
43
|
+
clip.run
|
44
|
+
# sox --combine mix music-track.mp3 narration.wav soundtrack.wav
|
45
|
+
```
|
46
|
+
|
47
|
+
Each input and output can also accept options, which ought to be chained onto
|
48
|
+
the corresponding input:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
clip = Chaussettes::Clip.new do |c|
|
52
|
+
c.mix
|
53
|
+
c.in('music-track.mp3').volume(0.5)
|
54
|
+
c.in 'narration.wav'
|
55
|
+
c.out('soundtrack.wav').bits(16).rate(44_100).channels(2)
|
56
|
+
end
|
57
|
+
|
58
|
+
clip.run
|
59
|
+
# sox --combine mix \
|
60
|
+
# --volume 0.5 music-track.mp3 \
|
61
|
+
# narration.wav \
|
62
|
+
# --bits 16 --rate 44100 --channels 2 soundtrack.wav
|
63
|
+
```
|
64
|
+
|
65
|
+
Furthermore, you can also chain effects to the clip:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
clip = Chaussettes::Clip.new do |c|
|
69
|
+
c.mix
|
70
|
+
c.in 'music-track.mp3'
|
71
|
+
c.in 'narration.wav'
|
72
|
+
c.out 'soundtrack.wav'
|
73
|
+
c.chain.trim(0, 10).pad(1.5)
|
74
|
+
end
|
75
|
+
# sox --combine mix \
|
76
|
+
# music-track.mp3 narration.wav soundtrack.wav \
|
77
|
+
# trim 0 10 pad 1.5
|
78
|
+
```
|
79
|
+
_(Note that not all effects supported by sox are implemented by Chaussettes...yet)_
|
80
|
+
|
81
|
+
And multiple effect chains may be applied, for potentially complex effects:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
clip = Chaussettes::Clip.new do |c|
|
85
|
+
c.mix
|
86
|
+
c.in 'music-track.mp3'
|
87
|
+
c.in 'narration.wav'
|
88
|
+
c.out 'soundtrack.wav'
|
89
|
+
c.chain.trim(0, 10).pad(1.5)
|
90
|
+
c.chain.newfile
|
91
|
+
c.chain.restart
|
92
|
+
end
|
93
|
+
# sox --combine mix \
|
94
|
+
# music-track.mp3 narration.wav soundtrack.wav \
|
95
|
+
# trim 0 10 pad 1.5 : newfile : restart
|
96
|
+
```
|
97
|
+
|
98
|
+
Inputs may be other clip instances, too:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
music = Chaussettes::Clip.new do |c|
|
102
|
+
c.in 'music-track.mp3'
|
103
|
+
c.out(device: :stdout).type(:wav)
|
104
|
+
c.chain.fade(0, 10, 0.5)
|
105
|
+
end
|
106
|
+
|
107
|
+
clip = Chaussettes::Clip.new do |c|
|
108
|
+
c.mix
|
109
|
+
c.in(music).volume(0.5).type(:wav)
|
110
|
+
c.in 'narration.wav'
|
111
|
+
c.out 'soundtrack.wav'
|
112
|
+
end
|
113
|
+
# sox --combine mix \
|
114
|
+
# --volume 0.5 --type wav "|sox music-track.mp3 --type wav - fade 0 10 0.5" \
|
115
|
+
# narration.wav soundtrack.wav
|
116
|
+
```
|
117
|
+
|
118
|
+
## Author
|
119
|
+
|
120
|
+
Jamis Buck <jamis@jamisbuck.org>
|
data/Rakefile
ADDED
data/examples/drum.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'chaussettes'
|
2
|
+
|
3
|
+
RHYTHM = [ 2, 1, 1, 2, 1, 0.5, 0.5, 2, 1, 1, 2, 1, 0.5, 0.5, 2 ].freeze
|
4
|
+
BEAT = 0.25
|
5
|
+
|
6
|
+
Chaussettes::Clip.new do |clip|
|
7
|
+
clip.show_progress(false)
|
8
|
+
clip.in(device: nil)
|
9
|
+
|
10
|
+
RHYTHM.each do |dur|
|
11
|
+
chain = clip.chain
|
12
|
+
chain.synth(BEAT * dur, :pluck) do |t|
|
13
|
+
t.start_tone nil, p1: 0.1
|
14
|
+
end
|
15
|
+
chain.synth(BEAT * dur, :noise) do |t|
|
16
|
+
t.combine :fmod
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
puts clip.command('play')
|
21
|
+
clip.play
|
22
|
+
end
|
data/examples/ducking.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# ruby ducking.rb track1 track2 output delay
|
2
|
+
#
|
3
|
+
# Mixes two tracks by reducing the volume of the first during the duration
|
4
|
+
# of the second. The second track begins after delay seconds. Saves the
|
5
|
+
# result as output.
|
6
|
+
|
7
|
+
require 'chaussettes'
|
8
|
+
|
9
|
+
track1 = ARGV[0] || abort("what's the first track?")
|
10
|
+
track2 = ARGV[1] || abort("what's the second track?")
|
11
|
+
delay = ARGV[2] || abort("what's the delay in seconds?")
|
12
|
+
output = ARGV[3] || abort("what's the output filename?")
|
13
|
+
|
14
|
+
info1 = Chaussettes::Info.new(track1)
|
15
|
+
info2 = Chaussettes::Info.new(track2)
|
16
|
+
delay = delay.to_f
|
17
|
+
|
18
|
+
if info1.duration < info2.duration + delay
|
19
|
+
abort 'first track is shorter than the second track!'
|
20
|
+
end
|
21
|
+
|
22
|
+
intro = Chaussettes::Clip.new do |clip|
|
23
|
+
clip.in(track1)
|
24
|
+
clip.out(device: :stdout).type(:wav)
|
25
|
+
clip.chain.fade(0, delay + 0.2, 0.4, type: :linear)
|
26
|
+
end
|
27
|
+
|
28
|
+
middle = Chaussettes::Clip.new do |clip|
|
29
|
+
clip.in(track1)
|
30
|
+
clip.out(device: :stdout).type(:wav)
|
31
|
+
clip.chain.
|
32
|
+
trim(delay, info2.duration).
|
33
|
+
vol(0.25).
|
34
|
+
pad(delay)
|
35
|
+
end
|
36
|
+
|
37
|
+
last = Chaussettes::Clip.new do |clip|
|
38
|
+
clip.in(track1)
|
39
|
+
clip.out(device: :stdout).type(:wav)
|
40
|
+
clip.chain.
|
41
|
+
trim(delay + info2.duration - 0.2).
|
42
|
+
fade(0.4, type: :linear).
|
43
|
+
pad(delay + info2.duration - 0.2)
|
44
|
+
end
|
45
|
+
|
46
|
+
overlay = Chaussettes::Clip.new do |clip|
|
47
|
+
clip.in(track2)
|
48
|
+
clip.out(device: :stdout).type(:wav)
|
49
|
+
clip.chain.pad(delay)
|
50
|
+
end
|
51
|
+
|
52
|
+
Chaussettes::Clip.new do |clip|
|
53
|
+
clip.mix
|
54
|
+
|
55
|
+
clip.in(intro).type(:wav)
|
56
|
+
clip.in(middle).type(:wav)
|
57
|
+
clip.in(last).type(:wav)
|
58
|
+
clip.in(overlay).type(:wav)
|
59
|
+
|
60
|
+
clip.out(output)
|
61
|
+
# ramp up the volume x4 to compensate for 4 clips being merged together
|
62
|
+
clip.chain.vol 4
|
63
|
+
|
64
|
+
clip.run
|
65
|
+
end
|
data/examples/synth.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'chaussettes'
|
2
|
+
|
3
|
+
Chaussettes::Clip.new do |clip|
|
4
|
+
clip.show_progress false
|
5
|
+
clip.in(device: nil)
|
6
|
+
|
7
|
+
notes = %w(C3 E3 G3 C4)
|
8
|
+
20.times do
|
9
|
+
clip.chain.synth(0.2, :pluck) do |t|
|
10
|
+
t.start_tone notes.sample, bias: 0, shift: rand(100), p1: 2
|
11
|
+
end.synth(0.2, :pluck) do |t|
|
12
|
+
t.combine :mix
|
13
|
+
t.start_tone notes.sample, bias: 0, shift: rand(100), p1: 2
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# show the command that will be run
|
18
|
+
puts clip.command('play')
|
19
|
+
|
20
|
+
# play it!
|
21
|
+
clip.play
|
22
|
+
end
|
data/lib/chaussettes.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'chaussettes/input'
|
2
|
+
require 'chaussettes/output'
|
3
|
+
require 'chaussettes/effect_chain'
|
4
|
+
require 'chaussettes/tool'
|
5
|
+
|
6
|
+
module Chaussettes
|
7
|
+
|
8
|
+
# encapsulates an invocation of sox
|
9
|
+
class Clip
|
10
|
+
def initialize
|
11
|
+
@combine = nil
|
12
|
+
@globals = []
|
13
|
+
@inputs = []
|
14
|
+
@output = nil
|
15
|
+
@effect_chains = []
|
16
|
+
|
17
|
+
yield self if block_given?
|
18
|
+
end
|
19
|
+
|
20
|
+
def command(cmd = 'sox')
|
21
|
+
raise 'a clip requires at least one input' if @inputs.empty?
|
22
|
+
Tool.new(cmd).tap { |sox| _build_command(sox) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
system(command.to_s)
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def play
|
31
|
+
system(command('play').to_s)
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def _build_command(sox)
|
36
|
+
sox << '--combine' << @combine if @combine
|
37
|
+
_append_globals sox
|
38
|
+
_append_inputs sox
|
39
|
+
_append_output sox
|
40
|
+
_append_effects sox
|
41
|
+
end
|
42
|
+
|
43
|
+
def _append_globals(sox)
|
44
|
+
@globals.each { |global| sox << global }
|
45
|
+
end
|
46
|
+
|
47
|
+
def _append_inputs(sox)
|
48
|
+
@inputs.each { |input| sox.concat(input.commands) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def _append_output(sox)
|
52
|
+
sox.concat(@output.commands) if @output
|
53
|
+
end
|
54
|
+
|
55
|
+
def _append_effects(sox)
|
56
|
+
@effect_chains.each.with_index do |chain, idx|
|
57
|
+
sox << ':' if idx > 0
|
58
|
+
sox.concat(chain.commands)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def combine(method)
|
63
|
+
@combine = method
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
def guard
|
68
|
+
@globals << '--guard'
|
69
|
+
self
|
70
|
+
end
|
71
|
+
|
72
|
+
def merge
|
73
|
+
@combine = :merge
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
def mix
|
78
|
+
@combine = :mix
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
def multiply
|
83
|
+
@combine = :multiply
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
def show_progress(flag)
|
88
|
+
@globals << (flag ? '--show-progress' : '--no-show-progress')
|
89
|
+
self
|
90
|
+
end
|
91
|
+
|
92
|
+
def repeatable
|
93
|
+
@globals << '-R'
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
def verbose(level)
|
98
|
+
@globals << "-V#{level}"
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
def in(source = nil, device: nil)
|
103
|
+
Input.new(source, device: device).tap do |input|
|
104
|
+
@inputs << input
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def out(dest = nil, device: nil)
|
109
|
+
@output = Output.new(dest, device: device)
|
110
|
+
end
|
111
|
+
|
112
|
+
def chain
|
113
|
+
EffectChain.new.tap do |chain|
|
114
|
+
@effect_chains << chain
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Chaussettes
|
2
|
+
|
3
|
+
# options that are common between input and output files
|
4
|
+
module CommonOptions
|
5
|
+
def bits(bits)
|
6
|
+
@arguments << '--bits' << bits
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def channels(channels)
|
11
|
+
@arguments << '--channels' << channels
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def encoding(encoding)
|
16
|
+
@arguments << '--encoding' << encoding
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def rate(rate)
|
21
|
+
@arguments << '--rate' << rate
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def type(type)
|
26
|
+
@arguments << '--type' << type
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def endian(option)
|
31
|
+
@arguments << '--endian' << option
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def reverse_nibbles
|
36
|
+
@arguments << '--reverse-nibbles'
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def reverse_bits
|
41
|
+
@arguments << '--reverse-bits'
|
42
|
+
self
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Chaussettes
|
2
|
+
module Effect
|
3
|
+
|
4
|
+
# Represents a fade effect
|
5
|
+
class Fade
|
6
|
+
TYPE_MAP = {
|
7
|
+
nil => nil,
|
8
|
+
:h => 'h', 'h' => 'h', :half_sine => 'h',
|
9
|
+
:l => 'l', 'l' => 'l', :log => 'l', :logarithmic => 'l',
|
10
|
+
:p => 'p', 'p' => 'p', :parabola => 'p', :inverted_parabola => 'p',
|
11
|
+
:q => 'q', 'q' => 'q', :quarter => 'q',
|
12
|
+
:t => 't', 't' => 't', :linear => 't', :triangle => 't'
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
attr_reader :commands
|
16
|
+
|
17
|
+
def initialize(in_len, stop_at = nil, out_len = nil, type: nil)
|
18
|
+
real_type = TYPE_MAP.fetch(type)
|
19
|
+
|
20
|
+
@commands = [ 'fade' ]
|
21
|
+
@commands << real_type if real_type
|
22
|
+
@commands << in_len
|
23
|
+
@commands << stop_at if stop_at
|
24
|
+
@commands << out_len if stop_at && out_len
|
25
|
+
|
26
|
+
@commands.freeze
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Chaussettes
|
2
|
+
module Effect
|
3
|
+
|
4
|
+
# Represents a gain effect
|
5
|
+
class Gain
|
6
|
+
TYPE_MAP = {
|
7
|
+
:e => 'e', 'e' => 'e', :equalize => 'e',
|
8
|
+
:B => 'B', 'B' => 'B', :balance => 'B',
|
9
|
+
:b => 'b', 'b' => 'b', :balance_protect => 'b',
|
10
|
+
:r => 'r', 'r' => 'r', :reclaim_headroom => 'r',
|
11
|
+
:n => 'n', 'n' => 'n', :normalize => 'n',
|
12
|
+
:l => 'l', 'l' => 'l', :limiter => 'l',
|
13
|
+
:h => 'h', 'h' => 'h', :headroom => 'h'
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
attr_reader :commands
|
17
|
+
|
18
|
+
def initialize(db, *opts)
|
19
|
+
@commands = [ 'gain' ]
|
20
|
+
@commands.concat(opts.map { |opt| TYPE_MAP.fetch(opt) })
|
21
|
+
@commands << db
|
22
|
+
@commands.freeze
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Chaussettes
|
2
|
+
module Effect
|
3
|
+
|
4
|
+
# Represents a synth effect
|
5
|
+
class Synth
|
6
|
+
attr_reader :commands
|
7
|
+
|
8
|
+
def initialize(length = nil, type = nil)
|
9
|
+
@length = length
|
10
|
+
@type = type
|
11
|
+
|
12
|
+
@just_intonation = nil
|
13
|
+
@combine = nil
|
14
|
+
@start_tone = nil
|
15
|
+
@end_tone = nil
|
16
|
+
@headroom = true
|
17
|
+
|
18
|
+
yield self if block_given?
|
19
|
+
|
20
|
+
_build_commands
|
21
|
+
end
|
22
|
+
|
23
|
+
def _build_commands
|
24
|
+
@commands = [ 'synth' ]
|
25
|
+
@commands << '-j' << @just_intonation if @just_intonation
|
26
|
+
@commands << '-n' unless @headroom
|
27
|
+
|
28
|
+
_append_start_params
|
29
|
+
|
30
|
+
@commands << @type if @type
|
31
|
+
@commands << @combine if @combine
|
32
|
+
|
33
|
+
_append_tones
|
34
|
+
|
35
|
+
@commands.freeze
|
36
|
+
end
|
37
|
+
|
38
|
+
def _append_start_params
|
39
|
+
return unless @length
|
40
|
+
@commands << @length
|
41
|
+
_append_opts @start_opts
|
42
|
+
end
|
43
|
+
|
44
|
+
OPT_NAMES = %i(bias shift p1 p2 p3).freeze
|
45
|
+
DEFAULTS = { bias: 0, shift: 0 }.freeze
|
46
|
+
|
47
|
+
def _append_opts(opts)
|
48
|
+
return unless opts && opts.any?
|
49
|
+
|
50
|
+
OPT_NAMES.each do |opt|
|
51
|
+
value = opts[opt] || DEFAULTS[opt]
|
52
|
+
break unless value
|
53
|
+
@commands << value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def _append_tones
|
58
|
+
return unless @start_tone
|
59
|
+
|
60
|
+
tone = if @end_tone
|
61
|
+
"#{@start_tone}#{@sweep}#{@end_tone}"
|
62
|
+
else
|
63
|
+
@start_tone
|
64
|
+
end
|
65
|
+
|
66
|
+
@commands << tone
|
67
|
+
|
68
|
+
_append_opts @end_opts
|
69
|
+
end
|
70
|
+
|
71
|
+
def headroom(enabled)
|
72
|
+
@headroom = enabled
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def just_intonation(semitones)
|
77
|
+
@just_intonation = semitones
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
def combine(method)
|
82
|
+
@combine = method
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
def length(len)
|
87
|
+
@length = len
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
def type(type)
|
92
|
+
@type = type
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
# options are:
|
97
|
+
# bias:
|
98
|
+
# shift:
|
99
|
+
# p1:
|
100
|
+
# p2:
|
101
|
+
# p3:
|
102
|
+
def start_tone(start_tone, opts = {})
|
103
|
+
@start_tone = start_tone
|
104
|
+
@start_opts = opts
|
105
|
+
self
|
106
|
+
end
|
107
|
+
|
108
|
+
# options are:
|
109
|
+
# bias:
|
110
|
+
# shift:
|
111
|
+
# p1:
|
112
|
+
# p2:
|
113
|
+
# p3:
|
114
|
+
def end_tone(end_tone, sweep = :linear, opts = {})
|
115
|
+
@end_tone = end_tone
|
116
|
+
@sweep = SWEEPS.fetch(sweep, sweep)
|
117
|
+
@end_opts = opts
|
118
|
+
self
|
119
|
+
end
|
120
|
+
|
121
|
+
SWEEPS = {
|
122
|
+
linear: ':',
|
123
|
+
square: '+',
|
124
|
+
exponential: '/', exp: '/',
|
125
|
+
exp2: '-'
|
126
|
+
}.freeze
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Chaussettes
|
2
|
+
module Effect
|
3
|
+
|
4
|
+
# Represents a volume effect
|
5
|
+
class Vol
|
6
|
+
attr_reader :commands
|
7
|
+
|
8
|
+
def initialize(gain, type: nil, limitergain: nil)
|
9
|
+
@commands = [ 'vol' ]
|
10
|
+
@commands << gain
|
11
|
+
@commands << type if type
|
12
|
+
@commands << limitergain if type && limitergain
|
13
|
+
@commands.freeze
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'chaussettes/effect/fade'
|
2
|
+
require 'chaussettes/effect/gain'
|
3
|
+
require 'chaussettes/effect/synth'
|
4
|
+
require 'chaussettes/effect/vol'
|
5
|
+
|
6
|
+
module Chaussettes
|
7
|
+
|
8
|
+
# A chain of effects to apply
|
9
|
+
class EffectChain
|
10
|
+
attr_reader :commands
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@commands = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def fade(in_len, stop_at = nil, out_len = nil, type: nil)
|
17
|
+
effect = Effect::Fade.new(in_len, stop_at, out_len, type: type)
|
18
|
+
@commands.concat(effect.commands)
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def gain(db, *opts)
|
23
|
+
effect = Effect::Gain.new(db, *opts)
|
24
|
+
@commands.concat(effect.commands)
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def newfile
|
29
|
+
@commands << 'newfile'
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def pad(length, position = nil)
|
34
|
+
length = "#{length}@#{position}" if position
|
35
|
+
@commands << 'pad' << length
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def restart
|
40
|
+
@commands << 'restart'
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def synth(length = nil, type = nil, &block)
|
45
|
+
effect = Effect::Synth.new(length, type, &block)
|
46
|
+
@commands.concat(effect.commands)
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def trim(*positions)
|
51
|
+
if positions.empty?
|
52
|
+
raise ArgumentError, 'you must specify at least one position for trim'
|
53
|
+
end
|
54
|
+
|
55
|
+
@commands << 'trim'
|
56
|
+
@commands.concat(positions)
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def vol(gain, type: nil, limitergain: nil)
|
61
|
+
effect = Effect::Vol.new(gain, type: type, limitergain: limitergain)
|
62
|
+
@commands.concat(effect.commands)
|
63
|
+
self
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'chaussettes/tool'
|
2
|
+
|
3
|
+
module Chaussettes
|
4
|
+
|
5
|
+
# encapsulates info about an audio file
|
6
|
+
class Info
|
7
|
+
def initialize(filename)
|
8
|
+
command = Tool.new('soxi') << filename
|
9
|
+
output = `#{command}`
|
10
|
+
@data = _parse(output)
|
11
|
+
end
|
12
|
+
|
13
|
+
def _parse(output)
|
14
|
+
output.lines.each.with_object({}) do |line, hash|
|
15
|
+
next if line.strip.empty?
|
16
|
+
key, value = line.split(/:/, 2)
|
17
|
+
hash[key.strip] = value.strip
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def filename
|
22
|
+
@_filename ||= @data['Input File']
|
23
|
+
end
|
24
|
+
|
25
|
+
def channels
|
26
|
+
@_channels ||= @data['Channels'].to_i
|
27
|
+
end
|
28
|
+
|
29
|
+
def rate
|
30
|
+
@_rate ||= @data['Sample Rate'].to_i
|
31
|
+
end
|
32
|
+
|
33
|
+
def bits
|
34
|
+
@_bits ||= @data['Precision'].to_i
|
35
|
+
end
|
36
|
+
|
37
|
+
def duration
|
38
|
+
@_duration ||= begin
|
39
|
+
timespec = @data['Duration'].split(/ /).first
|
40
|
+
h, m, s = timespec.split(/:/)
|
41
|
+
|
42
|
+
h.to_i * 3600 +
|
43
|
+
m.to_i * 60 +
|
44
|
+
s.to_f
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def size
|
49
|
+
@_size ||= @data['File Size']
|
50
|
+
end
|
51
|
+
|
52
|
+
def bit_rate
|
53
|
+
@_bit_rate ||= @data['Bit Rate']
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'chaussettes/common_options'
|
2
|
+
|
3
|
+
module Chaussettes
|
4
|
+
|
5
|
+
# Represents an input to an operation
|
6
|
+
class Input
|
7
|
+
include CommonOptions
|
8
|
+
|
9
|
+
def initialize(source = nil, device: nil)
|
10
|
+
@source = _translate_source(source) ||
|
11
|
+
_translate_device(device) ||
|
12
|
+
raise(ArgumentError, 'unsupported source')
|
13
|
+
|
14
|
+
@arguments = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def _translate_source(source)
|
18
|
+
if source.is_a?(String)
|
19
|
+
source
|
20
|
+
elsif source.respond_to?(:command)
|
21
|
+
"|#{source.command}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def _translate_device(device)
|
26
|
+
if device == :default
|
27
|
+
'--default-device'
|
28
|
+
elsif device == :stdin
|
29
|
+
'-'
|
30
|
+
elsif device.nil? || device == :null
|
31
|
+
'--null'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def commands
|
36
|
+
[ *@arguments, @source ]
|
37
|
+
end
|
38
|
+
|
39
|
+
def ignore_length
|
40
|
+
@arguments << '--ignore-length'
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def volume(factor)
|
45
|
+
@arguments << '--volume' << factor
|
46
|
+
self
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'chaussettes/common_options'
|
2
|
+
|
3
|
+
module Chaussettes
|
4
|
+
|
5
|
+
# Represents the output of an operation
|
6
|
+
class Output
|
7
|
+
include CommonOptions
|
8
|
+
|
9
|
+
def initialize(dest = nil, device: nil)
|
10
|
+
@dest = _translate_dest(dest) ||
|
11
|
+
_translate_device(device) ||
|
12
|
+
raise(ArgumentError, 'unsupported dest/device')
|
13
|
+
|
14
|
+
@arguments = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def _translate_dest(dest)
|
18
|
+
dest ? dest.to_s : nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def _translate_device(device)
|
22
|
+
if device == :pipe
|
23
|
+
'--sox-pipe'
|
24
|
+
elsif device == :default
|
25
|
+
'--default-device'
|
26
|
+
elsif device.nil? || device == :null
|
27
|
+
'--null'
|
28
|
+
elsif device == :stdout
|
29
|
+
'-'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def commands
|
34
|
+
[ *@arguments, @dest ]
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_comment(text)
|
38
|
+
@arguments << '--add-comment' << text
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def comment(text)
|
43
|
+
@arguments << '--comment' << text
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def compression(factor)
|
48
|
+
@arguments << '--compression' << factor
|
49
|
+
self
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module Chaussettes
|
4
|
+
|
5
|
+
# a generic wrapper for sox audio toolchain
|
6
|
+
class Tool
|
7
|
+
def initialize(command)
|
8
|
+
@command = command
|
9
|
+
@arguments = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def <<(arg)
|
13
|
+
@arguments << arg
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def concat(args)
|
18
|
+
@arguments.concat(args)
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
Shellwords.join([ @command, *@arguments ])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'chaussettes/clip'
|
3
|
+
|
4
|
+
class ClipTest < Minitest::Test
|
5
|
+
def setup
|
6
|
+
@clip = Chaussettes::Clip.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_merge_becomes_combine_option
|
10
|
+
@clip.merge
|
11
|
+
@clip.in
|
12
|
+
@clip.out
|
13
|
+
|
14
|
+
assert_equal 'sox --combine merge --null --null', @clip.command.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_mix_becomes_combine_option
|
18
|
+
@clip.mix
|
19
|
+
@clip.in
|
20
|
+
@clip.out
|
21
|
+
|
22
|
+
assert_equal 'sox --combine mix --null --null', @clip.command.to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_multiply_becomes_combine_option
|
26
|
+
@clip.multiply
|
27
|
+
@clip.in
|
28
|
+
@clip.out
|
29
|
+
|
30
|
+
assert_equal 'sox --combine multiply --null --null', @clip.command.to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_combine_option
|
34
|
+
@clip.in
|
35
|
+
@clip.out
|
36
|
+
@clip.combine :concatenate
|
37
|
+
|
38
|
+
assert_equal 'sox --combine concatenate --null --null', @clip.command.to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_global_options
|
42
|
+
@clip.guard.repeatable.verbose(2)
|
43
|
+
@clip.in
|
44
|
+
@clip.out
|
45
|
+
|
46
|
+
assert_equal 'sox --guard -R -V2 --null --null', @clip.command.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_multiple_inputs_are_rendered
|
50
|
+
@clip.in('file1.wav')
|
51
|
+
@clip.in('file2.wav')
|
52
|
+
@clip.out
|
53
|
+
assert_equal 'sox file1.wav file2.wav --null', @clip.command.to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_output_is_rendered
|
57
|
+
@clip.in
|
58
|
+
@clip.out('out.wav')
|
59
|
+
assert_equal 'sox --null out.wav', @clip.command.to_s
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_chain_adds_effect_chain
|
63
|
+
@clip.in
|
64
|
+
@clip.out
|
65
|
+
@clip.chain.trim(0, 10).pad(1.5)
|
66
|
+
assert_equal 'sox --null --null trim 0 10 pad 1.5', @clip.command.to_s
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_multiply_effect_chains
|
70
|
+
@clip.in
|
71
|
+
@clip.out
|
72
|
+
@clip.chain.trim(0, 10).pad(1.5)
|
73
|
+
@clip.chain.newfile
|
74
|
+
@clip.chain.restart
|
75
|
+
assert_equal 'sox --null --null trim 0 10 pad 1.5 : newfile : restart',
|
76
|
+
@clip.command.to_s
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'chaussettes/input'
|
4
|
+
|
5
|
+
class InputTest < Minitest::Test
|
6
|
+
def test_init_with_string_should_use_string_as_source
|
7
|
+
input = Chaussettes::Input.new('input.wav')
|
8
|
+
assert_equal [ 'input.wav' ], input.commands
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_init_with_command_should_use_command_as_source
|
12
|
+
command = OpenStruct.new(command: 'cmd -x -y -z')
|
13
|
+
input = Chaussettes::Input.new(command)
|
14
|
+
assert_equal [ '|cmd -x -y -z' ], input.commands
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_init_with_no_arguments_should_use_null_input
|
18
|
+
input = Chaussettes::Input.new
|
19
|
+
assert_equal [ '--null' ], input.commands
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_init_with_nil_device_should_use_null_input
|
23
|
+
input = Chaussettes::Input.new(device: nil)
|
24
|
+
assert_equal [ '--null' ], input.commands
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_init_with_null_device_should_use_null_input
|
28
|
+
input = Chaussettes::Input.new(device: :null)
|
29
|
+
assert_equal [ '--null' ], input.commands
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_init_with_stdin_device_should_use_dash_input
|
33
|
+
input = Chaussettes::Input.new(device: :stdin)
|
34
|
+
assert_equal [ '-' ], input.commands
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_init_with_default_device_should_use_default_device
|
38
|
+
input = Chaussettes::Input.new(device: :default)
|
39
|
+
assert_equal [ '--default-device' ], input.commands
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_arguments_should_precede_input_source
|
43
|
+
input = Chaussettes::Input.new('hello.mp3').ignore_length.volume(0.5)
|
44
|
+
expect = [ '--ignore-length', '--volume', 0.5, 'hello.mp3' ]
|
45
|
+
assert_equal expect, input.commands
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_common_options_should_be_included
|
49
|
+
input = Chaussettes::Input.new('hello.mp3').
|
50
|
+
bits(8).channels(2).encoding('encoding').rate(44_100).type('mp3').
|
51
|
+
endian('little').reverse_nibbles.reverse_bits
|
52
|
+
|
53
|
+
expected = [
|
54
|
+
'--bits', 8, '--channels', 2, '--encoding', 'encoding', '--rate', 44_100,
|
55
|
+
'--type', 'mp3', '--endian', 'little', '--reverse-nibbles',
|
56
|
+
'--reverse-bits', 'hello.mp3'
|
57
|
+
]
|
58
|
+
|
59
|
+
assert_equal expected, input.commands
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'chaussettes/output'
|
3
|
+
|
4
|
+
class OutputTest < Minitest::Test
|
5
|
+
def test_init_with_string_should_use_string_as_dest
|
6
|
+
output = Chaussettes::Output.new('output.wav')
|
7
|
+
assert_equal [ 'output.wav' ], output.commands
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_init_with_no_arguments_should_use_null_output
|
11
|
+
output = Chaussettes::Output.new
|
12
|
+
assert_equal [ '--null' ], output.commands
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_init_with_nil_device_should_use_null_output
|
16
|
+
output = Chaussettes::Output.new(device: nil)
|
17
|
+
assert_equal [ '--null' ], output.commands
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_init_with_null_device_should_use_null_output
|
21
|
+
output = Chaussettes::Output.new(device: :null)
|
22
|
+
assert_equal [ '--null' ], output.commands
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_init_with_stdout_device_should_use_dash_output
|
26
|
+
output = Chaussettes::Output.new(device: :stdout)
|
27
|
+
assert_equal [ '-' ], output.commands
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_init_with_pipe_device_should_use_sox_pipe_output
|
31
|
+
output = Chaussettes::Output.new(device: :pipe)
|
32
|
+
assert_equal [ '--sox-pipe' ], output.commands
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_init_with_default_device_should_use_default_device
|
36
|
+
output = Chaussettes::Output.new(device: :default)
|
37
|
+
assert_equal [ '--default-device' ], output.commands
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_arguments_should_precede_output_dest
|
41
|
+
output = Chaussettes::Output.new('hello.mp3').
|
42
|
+
comment('cmt').add_comment('cmt2').compression(2)
|
43
|
+
expect = [ '--comment', 'cmt', '--add-comment', 'cmt2', '--compression', 2,
|
44
|
+
'hello.mp3' ]
|
45
|
+
assert_equal expect, output.commands
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_common_options_should_be_included
|
49
|
+
output = Chaussettes::Output.new('hello.mp3').
|
50
|
+
bits(8).channels(2).encoding('encoding').rate(44_100).type('mp3').
|
51
|
+
endian('little').reverse_nibbles.reverse_bits
|
52
|
+
|
53
|
+
expected = [
|
54
|
+
'--bits', 8, '--channels', 2, '--encoding', 'encoding', '--rate', 44_100,
|
55
|
+
'--type', 'mp3', '--endian', 'little', '--reverse-nibbles',
|
56
|
+
'--reverse-bits', 'hello.mp3'
|
57
|
+
]
|
58
|
+
|
59
|
+
assert_equal expected, output.commands
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'chaussettes/tool'
|
3
|
+
|
4
|
+
class ToolTest < Minitest::Test
|
5
|
+
def setup
|
6
|
+
@tool = Chaussettes::Tool.new('command')
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_empty_arguments_should_render_only_the_command
|
10
|
+
assert_equal 'command', @tool.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_appended_arguments_should_be_rendered
|
14
|
+
@tool << '-flag' << 'value'
|
15
|
+
assert_equal 'command -flag value', @tool.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_concatted_arguments_should_be_rendered
|
19
|
+
@tool.concat [ '-flag', 'value' ]
|
20
|
+
assert_equal 'command -flag value', @tool.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_special_chars_should_be_escaped
|
24
|
+
@tool << '-flag' << 'two words' << 'angry!'
|
25
|
+
assert_equal 'command -flag two\ words angry\!', @tool.to_s
|
26
|
+
end
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: chaussettes
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jamis Buck
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-01-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: A wrapper around the sox audio manipulation utility
|
42
|
+
email:
|
43
|
+
- jamis@jamisbuck.org
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- MIT-LICENSE
|
49
|
+
- README.md
|
50
|
+
- Rakefile
|
51
|
+
- examples/drum.rb
|
52
|
+
- examples/ducking.rb
|
53
|
+
- examples/synth.rb
|
54
|
+
- lib/chaussettes.rb
|
55
|
+
- lib/chaussettes/clip.rb
|
56
|
+
- lib/chaussettes/common_options.rb
|
57
|
+
- lib/chaussettes/effect/fade.rb
|
58
|
+
- lib/chaussettes/effect/gain.rb
|
59
|
+
- lib/chaussettes/effect/synth.rb
|
60
|
+
- lib/chaussettes/effect/vol.rb
|
61
|
+
- lib/chaussettes/effect_chain.rb
|
62
|
+
- lib/chaussettes/info.rb
|
63
|
+
- lib/chaussettes/input.rb
|
64
|
+
- lib/chaussettes/output.rb
|
65
|
+
- lib/chaussettes/tool.rb
|
66
|
+
- lib/chaussettes/version.rb
|
67
|
+
- test/chaussettes/clip_test.rb
|
68
|
+
- test/chaussettes/input_test.rb
|
69
|
+
- test/chaussettes/output_test.rb
|
70
|
+
- test/chaussettes/tool_test.rb
|
71
|
+
homepage: http://github.com/jamis/chaussettes
|
72
|
+
licenses:
|
73
|
+
- MIT
|
74
|
+
metadata: {}
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
requirements: []
|
90
|
+
rubyforge_project:
|
91
|
+
rubygems_version: 2.5.1
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: A wrapper around the sox audio manipulation utility
|
95
|
+
test_files:
|
96
|
+
- test/chaussettes/clip_test.rb
|
97
|
+
- test/chaussettes/input_test.rb
|
98
|
+
- test/chaussettes/output_test.rb
|
99
|
+
- test/chaussettes/tool_test.rb
|