scissor 0.0.22

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 / 2009-03-29
2
+
3
+ * initial release
4
+
data/README.rdoc ADDED
@@ -0,0 +1,78 @@
1
+ = Scissor
2
+
3
+ == Description
4
+
5
+ utility to chop sound files
6
+
7
+ supported file format:
8
+
9
+ * mp3
10
+ * wav
11
+
12
+ == Installation
13
+
14
+ === Requirements
15
+
16
+ * {FFmpeg}[http://ffmpeg.mplayerhq.hu/]
17
+ * {Ecasound}[http://www.eca.cx/ecasound/] 2.5.0 or higher
18
+
19
+ === Archive Installation
20
+
21
+ rake install
22
+
23
+ === Gem Installation
24
+
25
+ gem update --system
26
+ gem install gemcutter
27
+ gem tumble
28
+ gem install scissor
29
+
30
+ == Features/Problems
31
+
32
+ * When you concatenate two or more files, format(sample rate, bit rate, ...) mismatch causes unexpected changes to output file.
33
+
34
+ == Synopsis
35
+
36
+ === instantiate
37
+
38
+ foo = Scissor('foo.mp3')
39
+ bar = Scissor('bar.wav')
40
+
41
+ === concat
42
+
43
+ foo + bar > 'foobar.mp3'
44
+
45
+ === slice + concat
46
+
47
+ foo[10, 1] + bar[2, 3] > 'slicefoobar.mp3'
48
+
49
+ === slice + concat + loop
50
+
51
+ (foo[10, 1] + bar[2, 3]) * 4 > 'slicefoobarloop.mp3'
52
+
53
+ === split
54
+
55
+ (Scissor('sequence.mp3') / 16).first.to_file('split.mp3')
56
+
57
+ === replace first 10 seconds with 30 seconds of silence
58
+
59
+ foo.replace(0, 10, Scissor.silence(30)).to_file('replace.mp3')
60
+
61
+ === sequence + loop
62
+
63
+ seq = Scissor.sequence('x y xyz', 0.2)
64
+ seq.apply(:x => foo, :y => Proc.new { bar }, :z => foo.reverse) * 4 > 'sequence.wav'
65
+
66
+ === half the pitch
67
+
68
+ foo.pitch(50)
69
+
70
+ === mix
71
+
72
+ Scissor.mix([foo, bar], 'mix.mp3')
73
+
74
+ == Copyright
75
+
76
+ Author:: youpy <youpy@buycheapviagraonlinenow.com>
77
+ Copyright:: Copyright (c) 2009 youpy
78
+ License:: MIT
data/Rakefile ADDED
@@ -0,0 +1,107 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/clean'
4
+ require 'spec/rake/spectask'
5
+ require 'rake/packagetask'
6
+ require 'rake/gempackagetask'
7
+ require 'rake/rdoctask'
8
+ require 'rake/contrib/rubyforgepublisher'
9
+ require 'rake/contrib/sshpublisher'
10
+ require 'fileutils'
11
+ include FileUtils
12
+
13
+ NAME = "scissor"
14
+ AUTHOR = "youpy"
15
+ EMAIL = "youpy@buycheapviagraonlinenow.com"
16
+ DESCRIPTION = "utility to chop sound files"
17
+ RUBYFORGE_PROJECT = "scissor"
18
+ HOMEPATH = "http://github.com/youpy/scissor"
19
+ BIN_FILES = %w( )
20
+ VERS = "0.0.22"
21
+
22
+ REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
23
+ CLEAN.include ['**/.*.sw?', '*.gem', '.config']
24
+ RDOC_OPTS = [
25
+ '--title', "#{NAME} documentation",
26
+ "--charset", "utf-8",
27
+ "--opname", "index.html",
28
+ "--line-numbers",
29
+ "--main", "README.rdoc",
30
+ "--inline-source",
31
+ ]
32
+
33
+ task :default => [:spec]
34
+ task :package => [:clean]
35
+
36
+ Spec::Rake::SpecTask.new do |t|
37
+ t.spec_opts = ['--options', "spec/spec.opts"]
38
+ t.spec_files = FileList['spec/*_spec.rb']
39
+ t.rcov = true
40
+ end
41
+
42
+ spec = Gem::Specification.new do |s|
43
+ s.name = NAME
44
+ s.version = VERS
45
+ s.platform = Gem::Platform::RUBY
46
+ s.has_rdoc = true
47
+ s.extra_rdoc_files = ["README.rdoc", "ChangeLog"]
48
+ s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)/']
49
+ s.summary = DESCRIPTION
50
+ s.description = DESCRIPTION
51
+ s.author = AUTHOR
52
+ s.email = EMAIL
53
+ s.executables = BIN_FILES
54
+ s.rubyforge_project = RUBYFORGE_PROJECT
55
+ s.bindir = "bin"
56
+ s.require_path = "lib"
57
+ s.homepage = HOMEPATH
58
+ s.test_files = Dir["test/test_*.rb"]
59
+
60
+ s.add_dependency('open4')
61
+ s.add_dependency('ruby-mp3info')
62
+ s.add_dependency('riff')
63
+ s.add_dependency('tempdir')
64
+ #s.required_ruby_version = '>= 1.8.2'
65
+
66
+ s.files = %w(README.rdoc ChangeLog Rakefile) +
67
+ Dir.glob("{bin,doc,test,lib,data,templates,generator,extras,website,script}/**/*") +
68
+ Dir.glob("ext/**/*.{h,c,rb}") +
69
+ Dir.glob("examples/**/*.rb") +
70
+ Dir.glob("tools/*.rb")
71
+
72
+ s.extensions = FileList["ext/**/extconf.rb"].to_a
73
+ end
74
+
75
+ Rake::GemPackageTask.new(spec) do |p|
76
+ p.need_tar = true
77
+ p.gem_spec = spec
78
+ end
79
+
80
+ task :install do
81
+ name = "#{NAME}-#{VERS}.gem"
82
+ sh %{rake package}
83
+ sh %{sudo gem install pkg/#{name}}
84
+ end
85
+
86
+ task :uninstall => [:clean] do
87
+ sh %{sudo gem uninstall #{NAME}}
88
+ end
89
+
90
+ Rake::RDocTask.new do |rdoc|
91
+ rdoc.rdoc_dir = 'html'
92
+ rdoc.options += RDOC_OPTS
93
+ rdoc.template = "resh"
94
+ #rdoc.template = "#{ENV['template']}.rb" if ENV['template']
95
+ if ENV['DOC_FILES']
96
+ rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
97
+ else
98
+ rdoc.rdoc_files.include('README.rdoc', 'ChangeLog')
99
+ rdoc.rdoc_files.include('lib/**/*.rb')
100
+ rdoc.rdoc_files.include('ext/**/*.c')
101
+ end
102
+ end
103
+
104
+ desc 'Show information about the gem.'
105
+ task :debug_gem do
106
+ puts spec.to_ruby
107
+ end
data/data/silence.mp3 ADDED
Binary file
@@ -0,0 +1,183 @@
1
+ module Scissor
2
+ class Chunk
3
+ class Error < StandardError; end
4
+ class EmptyFragment < Error; end
5
+ class OutOfDuration < Error; end
6
+
7
+ attr_reader :fragments
8
+
9
+ def initialize(filename = nil)
10
+ @fragments = []
11
+
12
+ if filename
13
+ @fragments << Fragment.new(
14
+ filename,
15
+ 0,
16
+ SoundFile.new(filename).length)
17
+ end
18
+ end
19
+
20
+ def add_fragment(fragment)
21
+ @fragments << fragment
22
+ end
23
+
24
+ def add_fragments(fragments)
25
+ fragments.each do |fragment|
26
+ add_fragment(fragment)
27
+ end
28
+ end
29
+
30
+ def duration
31
+ @fragments.inject(0) do |memo, fragment|
32
+ memo += fragment.duration
33
+ end
34
+ end
35
+
36
+ def slice(start, length)
37
+ if start + length > duration
38
+ length = duration - start
39
+ end
40
+
41
+ new_instance = self.class.new
42
+ remaining_start = start.to_f
43
+ remaining_length = length.to_f
44
+
45
+ @fragments.each do |fragment|
46
+ new_fragment, remaining_start, remaining_length =
47
+ fragment.create(remaining_start, remaining_length)
48
+
49
+ if new_fragment
50
+ new_instance.add_fragment(new_fragment)
51
+ end
52
+
53
+ if remaining_length == 0
54
+ break
55
+ end
56
+ end
57
+
58
+ new_instance
59
+ end
60
+
61
+ alias [] slice
62
+
63
+ def concat(other)
64
+ add_fragments(other.fragments)
65
+
66
+ self
67
+ end
68
+
69
+ alias << concat
70
+
71
+ def +(other)
72
+ new_instance = Scissor()
73
+ new_instance.add_fragments(@fragments + other.fragments)
74
+ new_instance
75
+ end
76
+
77
+ def loop(count)
78
+ orig_fragments = @fragments.clone
79
+
80
+ (count - 1).times do
81
+ add_fragments(orig_fragments)
82
+ end
83
+
84
+ self
85
+ end
86
+
87
+ alias * loop
88
+
89
+ def split(count)
90
+ splitted_duration = duration / count.to_f
91
+ results = []
92
+
93
+ count.times do |i|
94
+ results << slice(i * splitted_duration, splitted_duration)
95
+ end
96
+
97
+ results
98
+ end
99
+
100
+ alias / split
101
+
102
+ def fill(filled_duration)
103
+ if @fragments.empty?
104
+ raise EmptyFragment
105
+ end
106
+
107
+ remain = filled_duration
108
+ new_instance = self.class.new
109
+
110
+ while !remain.zero? && filled_duration > new_instance.duration
111
+ if remain < duration
112
+ added = slice(0, remain)
113
+ else
114
+ added = self
115
+ end
116
+
117
+ new_instance += added
118
+ remain -= added.duration
119
+ end
120
+
121
+
122
+ new_instance
123
+ end
124
+
125
+ def replace(start, length, replaced)
126
+ new_instance = self.class.new
127
+ offset = start + length
128
+
129
+ if offset > duration
130
+ raise OutOfDuration
131
+ end
132
+
133
+ if start > 0
134
+ new_instance += slice(0, start)
135
+ end
136
+
137
+ new_instance += replaced
138
+ new_instance += slice(offset, duration - offset)
139
+
140
+ new_instance
141
+ end
142
+
143
+ def reverse
144
+ new_instance = self.class.new
145
+
146
+ @fragments.reverse.each do |fragment|
147
+ new_instance.add_fragment(Fragment.new(
148
+ fragment.filename,
149
+ fragment.start,
150
+ fragment.true_duration,
151
+ !fragment.reversed?,
152
+ fragment.pitch))
153
+ end
154
+
155
+ new_instance
156
+ end
157
+
158
+ def pitch(pitch)
159
+ new_instance = self.class.new
160
+
161
+ @fragments.each do |fragment|
162
+ new_instance.add_fragment(Fragment.new(
163
+ fragment.filename,
164
+ fragment.start,
165
+ fragment.true_duration,
166
+ fragment.reversed?,
167
+ fragment.pitch * (pitch.to_f / 100)))
168
+ end
169
+
170
+ new_instance
171
+ end
172
+
173
+ def to_file(filename, options = {})
174
+ Scissor.mix([self], filename, options)
175
+ end
176
+
177
+ alias > to_file
178
+
179
+ def >>(filename)
180
+ to_file(filename, :overwrite => true)
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,61 @@
1
+ require 'pathname'
2
+
3
+ module Scissor
4
+ class Fragment
5
+ attr_reader :filename, :start, :pitch
6
+
7
+ def initialize(filename, start, duration, reverse = false, pitch = 100)
8
+ @filename = Pathname.new(filename).realpath
9
+ @start = start
10
+ @duration = duration
11
+ @reverse = reverse
12
+ @pitch = pitch
13
+
14
+ freeze
15
+ end
16
+
17
+ def duration
18
+ @duration * (100 / pitch.to_f)
19
+ end
20
+
21
+ def true_duration
22
+ @duration
23
+ end
24
+
25
+ def reversed?
26
+ @reverse
27
+ end
28
+
29
+ def create(remaining_start, remaining_length)
30
+ new_fragment = nil
31
+
32
+ if remaining_start >= duration
33
+ remaining_start -= duration
34
+ else
35
+ if remaining_start + remaining_length >= duration
36
+ new_fragment = self.class.new(
37
+ filename,
38
+ start + remaining_start * pitch.to_f / 100,
39
+ (duration - remaining_start) * pitch.to_f / 100,
40
+ false,
41
+ pitch)
42
+
43
+ remaining_length -= duration - remaining_start
44
+ remaining_start = 0
45
+ else
46
+ new_fragment = self.class.new(
47
+ filename,
48
+ start + remaining_start * pitch.to_f / 100,
49
+ remaining_length * pitch.to_f / 100,
50
+ false,
51
+ pitch)
52
+
53
+ remaining_start = 0
54
+ remaining_length = 0
55
+ end
56
+ end
57
+
58
+ return new_fragment, remaining_start, remaining_length
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,7 @@
1
+ module Scissor
2
+ module Loggable
3
+ def logger
4
+ Scissor.logger
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,30 @@
1
+ module Scissor
2
+ class Sequence
3
+ def initialize(pattern, duration_per_step)
4
+ @pattern = pattern
5
+ @duration_per_step = duration_per_step
6
+ end
7
+
8
+ def apply(instruments)
9
+ @pattern.split(//).inject(Scissor()) do |result, c|
10
+ if instruments.include?(c.to_sym)
11
+ instrument = instruments[c.to_sym]
12
+
13
+ if instrument.is_a?(Proc)
14
+ instrument = instrument.call(c)
15
+ end
16
+
17
+ if @duration_per_step > instrument.duration
18
+ result += instrument + Scissor.silence(@duration_per_step - instrument.duration)
19
+ else
20
+ result += instrument.slice(0, @duration_per_step)
21
+ end
22
+ else
23
+ result += Scissor.silence(@duration_per_step)
24
+ end
25
+
26
+ result
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,34 @@
1
+ require 'mp3info'
2
+ require 'pathname'
3
+ require 'riff/reader'
4
+
5
+ module Scissor
6
+ class SoundFile
7
+ SUPPORTED_FORMATS = %w/mp3 wav/
8
+
9
+ class Error < StandardError; end
10
+ class UnknownFormat < Error; end
11
+
12
+ def initialize(filename)
13
+ @filename = Pathname.new(filename)
14
+ @ext = @filename.extname.sub(/^\./, '').downcase
15
+
16
+ unless SUPPORTED_FORMATS.include?(@ext)
17
+ raise UnknownFormat
18
+ end
19
+ end
20
+
21
+ def length
22
+ case @ext
23
+ when 'mp3'
24
+ Mp3Info.new(@filename).length
25
+ when 'wav'
26
+ riff = Riff::Reader.open(@filename ,"r")
27
+ data = riff.root_chunk['data']
28
+ fmt = riff.root_chunk['fmt ']
29
+
30
+ data.length / fmt.body.unpack('s2i2')[3].to_f
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,132 @@
1
+ require 'digest/md5'
2
+ require 'pathname'
3
+ require 'open4'
4
+ require 'temp_dir'
5
+
6
+ module Scissor
7
+ class Writer
8
+ include Loggable
9
+
10
+ class Error < StandardError; end
11
+ class FileExists < Error; end
12
+ class EmptyFragment < Error; end
13
+ class CommandFailed < Error; end
14
+
15
+ def initialize
16
+ @tracks = []
17
+
18
+ which('ecasound')
19
+ which('ffmpeg')
20
+ end
21
+
22
+ def add_track(fragments)
23
+ @tracks << fragments
24
+ end
25
+
26
+ def fragments_to_file(fragments, outfile, tmpdir)
27
+ position = 0.0
28
+ cmd = %w/ecasound/
29
+
30
+ fragments.each_with_index do |fragment, index|
31
+ fragment_filename = fragment.filename
32
+ fragment_duration = fragment.duration
33
+
34
+ if !index.zero? && (index % 80).zero?
35
+ run_command(cmd.join(' '))
36
+ cmd = %w/ecasound/
37
+ end
38
+
39
+ fragment_outfile =
40
+ fragment_filename.extname.downcase == '.wav' ? fragment_filename :
41
+ tmpdir + (Digest::MD5.hexdigest(fragment_filename) + '.wav')
42
+
43
+ unless fragment_outfile.exist?
44
+ run_command("ffmpeg -i \"#{fragment_filename}\" \"#{fragment_outfile}\"")
45
+ end
46
+
47
+ cmd <<
48
+ "-a:#{index} " +
49
+ "-i:" +
50
+ (fragment.reversed? ? 'reverse,' : '') +
51
+ "select,#{fragment.start},#{fragment.true_duration},\"#{fragment_outfile}\" " +
52
+ "-o:#{outfile} " +
53
+ (fragment.pitch.to_f == 100.0 ? "" : "-ei:#{fragment.pitch} ") +
54
+ "-y:#{position}"
55
+
56
+ position += fragment_duration
57
+ end
58
+
59
+ run_command(cmd.join(' '))
60
+ end
61
+
62
+ def mix_files(filenames, outfile)
63
+ cmd = %w/ecasound/
64
+
65
+ filenames.each_with_index do |tf, index|
66
+ cmd << "-a:#{index} -i:#{tf}"
67
+ end
68
+
69
+ cmd << "-a:all -o:#{outfile}"
70
+ run_command(cmd.join(' '))
71
+ end
72
+
73
+ def to_file(filename, options)
74
+ filename = Pathname.new(filename)
75
+
76
+ if @tracks.flatten.empty?
77
+ raise EmptyFragment
78
+ end
79
+
80
+ options = {
81
+ :overwrite => false,
82
+ :bitrate => '128k'
83
+ }.merge(options)
84
+
85
+ if filename.exist?
86
+ if options[:overwrite]
87
+ filename.unlink
88
+ else
89
+ raise FileExists
90
+ end
91
+ end
92
+
93
+ TempDir.create do |dir|
94
+ tmpdir = Pathname.new(dir)
95
+ tmpfiles = []
96
+
97
+ @tracks.each_with_index do |fragments, track_index|
98
+ tmpfiles << tmpfile = tmpdir + 'track_%s.wav' % track_index.to_s
99
+ fragments_to_file(fragments, tmpfile, tmpdir)
100
+ end
101
+
102
+ mix_files(tmpfiles, final_tmpfile = tmpdir + 'tmp.wav')
103
+
104
+ if filename.extname == '.wav'
105
+ File.rename(final_tmpfile, filename)
106
+ else
107
+ run_command("ffmpeg -ab #{options[:bitrate]} -i \"#{final_tmpfile}\" \"#{filename}\"")
108
+ end
109
+ end
110
+ end
111
+
112
+ def which(command)
113
+ run_command("which #{command}")
114
+ end
115
+
116
+ def run_command(cmd)
117
+ logger.debug("run_command: #{cmd}")
118
+
119
+ result = ''
120
+ status = Open4.popen4(cmd) do |pid, stdin, stdout, stderr|
121
+ logger.debug(stderr.read)
122
+ result = stdout.read
123
+ end
124
+
125
+ if status.exitstatus != 0
126
+ raise CommandFailed.new(cmd)
127
+ end
128
+
129
+ return result
130
+ end
131
+ end
132
+ end
data/lib/scissor.rb ADDED
@@ -0,0 +1,55 @@
1
+ require 'scissor/loggable'
2
+ require 'scissor/chunk'
3
+ require 'scissor/fragment'
4
+ require 'scissor/sound_file'
5
+ require 'scissor/sequence'
6
+ require 'scissor/writer'
7
+
8
+ def Scissor(*args)
9
+ Scissor::Chunk.new(*args)
10
+ end
11
+
12
+ require 'logger'
13
+
14
+ module Scissor
15
+ @logger = Logger.new(STDOUT)
16
+ @logger.level = Logger::INFO
17
+
18
+ class << self
19
+ attr_accessor :logger
20
+ end
21
+
22
+ def logger
23
+ self.class.logger
24
+ end
25
+
26
+ class << self
27
+ def silence(duration)
28
+ Scissor(File.dirname(__FILE__) + '/../data/silence.mp3').
29
+ slice(0, 1).
30
+ fill(duration)
31
+ end
32
+
33
+ def sequence(*args)
34
+ Scissor::Sequence.new(*args)
35
+ end
36
+
37
+ def join(scissor_array)
38
+ scissor_array.inject(Scissor()) do |m, scissor|
39
+ m + scissor
40
+ end
41
+ end
42
+
43
+ def mix(scissor_array, filename, options = {})
44
+ writer = Scissor::Writer.new
45
+
46
+ scissor_array.each do |scissor|
47
+ writer.add_track(scissor.fragments)
48
+ end
49
+
50
+ writer.to_file(filename, options)
51
+
52
+ Scissor(filename)
53
+ end
54
+ end
55
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scissor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.22
5
+ platform: ruby
6
+ authors:
7
+ - youpy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-11 00:00:00 +09:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: open4
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: ruby-mp3info
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: riff
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: tempdir
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ description: utility to chop sound files
56
+ email: youpy@buycheapviagraonlinenow.com
57
+ executables: []
58
+
59
+ extensions: []
60
+
61
+ extra_rdoc_files:
62
+ - README.rdoc
63
+ - ChangeLog
64
+ files:
65
+ - README.rdoc
66
+ - ChangeLog
67
+ - Rakefile
68
+ - lib/scissor/chunk.rb
69
+ - lib/scissor/fragment.rb
70
+ - lib/scissor/loggable.rb
71
+ - lib/scissor/sequence.rb
72
+ - lib/scissor/sound_file.rb
73
+ - lib/scissor/writer.rb
74
+ - lib/scissor.rb
75
+ - data/silence.mp3
76
+ has_rdoc: true
77
+ homepage: http://github.com/youpy/scissor
78
+ licenses: []
79
+
80
+ post_install_message:
81
+ rdoc_options:
82
+ - --title
83
+ - scissor documentation
84
+ - --charset
85
+ - utf-8
86
+ - --opname
87
+ - index.html
88
+ - --line-numbers
89
+ - --main
90
+ - README.rdoc
91
+ - --inline-source
92
+ - --exclude
93
+ - ^(examples|extras)/
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: "0"
101
+ version:
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: "0"
107
+ version:
108
+ requirements: []
109
+
110
+ rubyforge_project: scissor
111
+ rubygems_version: 1.3.5
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: utility to chop sound files
115
+ test_files: []
116
+