scissor 0.0.22

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