youpy-scissor 0.0.19 → 0.0.20

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -18,12 +18,12 @@ supported file format:
18
18
 
19
19
  === Archive Installation
20
20
 
21
- rake install
21
+ rake install
22
22
 
23
23
  === Gem Installation
24
24
 
25
- gem sources -a http://gems.github.com/
26
- gem install youpy-scissor
25
+ gem sources -a http://gems.github.com/
26
+ gem install youpy-scissor
27
27
 
28
28
  == Features/Problems
29
29
 
@@ -31,27 +31,30 @@ supported file format:
31
31
 
32
32
  == Synopsis
33
33
 
34
- foo = Scissor('foo.mp3')
35
- bar = Scissor('bar.wav')
34
+ foo = Scissor('foo.mp3')
35
+ bar = Scissor('bar.wav')
36
+
37
+ # concat
38
+ foo + bar > 'foobar.mp3'
36
39
 
37
- # concat
38
- foo + bar > 'foobar.mp3'
40
+ # slice + concat
41
+ foo[10, 1] + bar[2, 3] > 'slicefoobar.mp3'
39
42
 
40
- # slice + concat
41
- foo[10, 1] + bar[2, 3] > 'slicefoobar.mp3'
43
+ # slice + concat + loop
44
+ (foo[10, 1] + bar[2, 3]) * 4 > 'slicefoobarloop.mp3'
42
45
 
43
- # slice + concat + loop
44
- (foo[10, 1] + bar[2, 3]) * 4 > 'slicefoobarloop.mp3'
46
+ # split
47
+ (Scissor('sequence.mp3') / 16).first.to_file('split.mp3')
45
48
 
46
- # split
47
- (Scissor('sequence.mp3') / 16).first.to_file('split.mp3')
49
+ # replace first 10 seconds with 30 seconds of silence
50
+ foo.replace(0, 10, Scissor.silence(30)).to_file('replace.mp3')
48
51
 
49
- # replace first 10 seconds with 30 seconds of silence
50
- foo.replace(0, 10, Scissor.silence(30)).to_file('replace.mp3')
52
+ # sequence + loop
53
+ seq = Scissor.sequence('x y xyz', 0.2)
54
+ seq.apply(:x => foo, :y => Proc.new { bar }, :z => foo.reverse) * 4 > 'sequence.wav'
51
55
 
52
- # sequence + loop
53
- seq = Scissor.sequence('x y xyz', 0.2)
54
- seq.apply(:x => foo, :y => Proc.new { bar }, :z => foo.reverse) * 4 > 'sequence.wav'
56
+ # mix
57
+ Scissor.mix([foo, bar], 'mix.mp3')
55
58
 
56
59
  == Copyright
57
60
 
data/Rakefile CHANGED
@@ -17,7 +17,7 @@ DESCRIPTION = "utility to chop sound files"
17
17
  RUBYFORGE_PROJECT = "scissor"
18
18
  HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
19
19
  BIN_FILES = %w( )
20
- VERS = "0.0.19"
20
+ VERS = "0.0.20"
21
21
 
22
22
  REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
23
23
  CLEAN.include ['**/.*.sw?', '*.gem', '.config']
@@ -87,7 +87,6 @@ task :uninstall => [:clean] do
87
87
  sh %{sudo gem uninstall #{NAME}}
88
88
  end
89
89
 
90
-
91
90
  Rake::RDocTask.new do |rdoc|
92
91
  rdoc.rdoc_dir = 'html'
93
92
  rdoc.options += RDOC_OPTS
data/lib/scissor.rb CHANGED
@@ -1,26 +1,55 @@
1
+ require 'scissor/loggable'
1
2
  require 'scissor/chunk'
2
3
  require 'scissor/fragment'
3
4
  require 'scissor/sound_file'
4
5
  require 'scissor/sequence'
6
+ require 'scissor/writer'
5
7
 
6
8
  def Scissor(*args)
7
9
  Scissor::Chunk.new(*args)
8
10
  end
9
11
 
12
+ require 'logger'
13
+
10
14
  module Scissor
11
- def self.silence(duration)
12
- Scissor(File.dirname(__FILE__) + '/../data/silence.mp3').
13
- slice(0, 1).
14
- fill(duration)
15
+ @logger = Logger.new(STDOUT)
16
+ @logger.level = Logger::INFO
17
+
18
+ class << self
19
+ attr_accessor :logger
15
20
  end
16
21
 
17
- def self.sequence(*args)
18
- Scissor::Sequence.new(*args)
22
+ def logger
23
+ self.class.logger
19
24
  end
20
25
 
21
- def self.join(scissor_array)
22
- scissor_array.inject(Scissor()) do |m, scissor|
23
- m + scissor
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)
24
53
  end
25
54
  end
26
55
  end
data/lib/scissor/chunk.rb CHANGED
@@ -1,23 +1,11 @@
1
1
  require 'digest/md5'
2
2
  require 'pathname'
3
- require 'open4'
4
- require 'logger'
5
- require 'temp_dir'
6
3
 
7
4
  module Scissor
8
5
  class Chunk
9
- @logger = Logger.new(STDOUT)
10
- @logger.level = Logger::INFO
11
-
12
- class << self
13
- attr_accessor :logger
14
- end
15
-
16
6
  class Error < StandardError; end
17
- class FileExists < Error; end
18
7
  class EmptyFragment < Error; end
19
8
  class OutOfDuration < Error; end
20
- class CommandFailed < Error; end
21
9
 
22
10
  attr_reader :fragments
23
11
 
@@ -170,72 +158,10 @@ module Scissor
170
158
  end
171
159
 
172
160
  def to_file(filename, options = {})
173
- filename = Pathname.new(filename)
174
-
175
- if @fragments.empty?
176
- raise EmptyFragment
177
- end
178
-
179
- which('ecasound')
180
- which('ffmpeg')
181
-
182
- options = {
183
- :overwrite => false
184
- }.merge(options)
185
-
186
- filename = Pathname.new(filename)
187
-
188
- if filename.exist?
189
- if options[:overwrite]
190
- filename.unlink
191
- else
192
- raise FileExists
193
- end
194
- end
195
-
196
- position = 0.0
197
-
198
- TempDir.create do |dir|
199
- tmpdir = Pathname.new(dir)
200
- tmpfile = tmpdir + 'tmp.wav'
201
- cmd = %w/ecasound/
202
-
203
- @fragments.each_with_index do |fragment, index|
204
- fragment_filename = fragment.filename
205
- fragment_duration = fragment.duration
206
-
207
- if !index.zero? && (index % 80).zero?
208
- run_command(cmd.join(' '))
209
- cmd = %w/ecasound/
210
- end
211
-
212
- fragment_tmpfile =
213
- fragment_filename.extname.downcase == '.wav' ? fragment_filename :
214
- tmpdir + (Digest::MD5.hexdigest(fragment_filename) + '.wav')
161
+ writer = Writer.new
215
162
 
216
- unless fragment_tmpfile.exist?
217
- run_command("ffmpeg -i \"#{fragment_filename}\" \"#{fragment_tmpfile}\"")
218
- end
219
-
220
- cmd <<
221
- "-a:#{index} " +
222
- "-i:" +
223
- (fragment.reversed? ? 'reverse,' : '') +
224
- "select,#{fragment.start},#{fragment_duration},\"#{fragment_tmpfile}\" " +
225
- "-o:#{tmpfile} " +
226
- "-y:#{position}"
227
-
228
- position += fragment_duration
229
- end
230
-
231
- run_command(cmd.join(' '))
232
-
233
- if filename.extname == '.wav'
234
- File.rename(tmpfile, filename)
235
- else
236
- run_command("ffmpeg -i \"#{tmpfile}\" \"#{filename}\"")
237
- end
238
- end
163
+ writer.add_track(@fragments)
164
+ writer.to_file(filename, options)
239
165
 
240
166
  self.class.new(filename)
241
167
  end
@@ -245,29 +171,5 @@ module Scissor
245
171
  def >>(filename)
246
172
  to_file(filename, :overwrite => true)
247
173
  end
248
-
249
- def which(command)
250
- run_command("which #{command}")
251
- end
252
-
253
- def run_command(cmd)
254
- logger.debug("run_command: #{cmd}")
255
-
256
- result = ''
257
- status = Open4.popen4(cmd) do |pid, stdin, stdout, stderr|
258
- logger.debug(stderr.read)
259
- result = stdout.read
260
- end
261
-
262
- if status.exitstatus != 0
263
- raise CommandFailed.new(cmd)
264
- end
265
-
266
- return result
267
- end
268
-
269
- def logger
270
- self.class.logger
271
- end
272
174
  end
273
175
  end
@@ -0,0 +1,7 @@
1
+ module Scissor
2
+ module Loggable
3
+ def logger
4
+ Scissor.logger
5
+ end
6
+ end
7
+ end
@@ -6,9 +6,7 @@ module Scissor
6
6
  end
7
7
 
8
8
  def apply(instruments)
9
- result = Scissor()
10
-
11
- @pattern.split(//).each do |c|
9
+ @pattern.split(//).inject(Scissor()) do |result, c|
12
10
  if instruments.include?(c.to_sym)
13
11
  instrument = instruments[c.to_sym]
14
12
 
@@ -17,17 +15,16 @@ module Scissor
17
15
  end
18
16
 
19
17
  if @duration_per_step > instrument.duration
20
- result += instrument
21
- result += Scissor.silence(@duration_per_step - instrument.duration)
18
+ result += instrument + Scissor.silence(@duration_per_step - instrument.duration)
22
19
  else
23
20
  result += instrument.slice(0, @duration_per_step)
24
21
  end
25
22
  else
26
23
  result += Scissor.silence(@duration_per_step)
27
24
  end
28
- end
29
25
 
30
- result
26
+ result
27
+ end
31
28
  end
32
29
  end
33
30
  end
@@ -0,0 +1,128 @@
1
+ require 'open4'
2
+ require 'temp_dir'
3
+
4
+ module Scissor
5
+ class Writer
6
+ include Loggable
7
+
8
+ class Error < StandardError; end
9
+ class FileExists < Error; end
10
+ class EmptyFragment < Error; end
11
+ class CommandFailed < Error; end
12
+
13
+ def initialize
14
+ @tracks = []
15
+
16
+ which('ecasound')
17
+ which('ffmpeg')
18
+ end
19
+
20
+ def add_track(fragments)
21
+ @tracks << fragments
22
+ end
23
+
24
+ def fragments_to_file(fragments, outfile, tmpdir)
25
+ position = 0.0
26
+ cmd = %w/ecasound/
27
+
28
+ fragments.each_with_index do |fragment, index|
29
+ fragment_filename = fragment.filename
30
+ fragment_duration = fragment.duration
31
+
32
+ if !index.zero? && (index % 80).zero?
33
+ run_command(cmd.join(' '))
34
+ cmd = %w/ecasound/
35
+ end
36
+
37
+ fragment_outfile =
38
+ fragment_filename.extname.downcase == '.wav' ? fragment_filename :
39
+ tmpdir + (Digest::MD5.hexdigest(fragment_filename) + '.wav')
40
+
41
+ unless fragment_outfile.exist?
42
+ run_command("ffmpeg -i \"#{fragment_filename}\" \"#{fragment_outfile}\"")
43
+ end
44
+
45
+ cmd <<
46
+ "-a:#{index} " +
47
+ "-i:" +
48
+ (fragment.reversed? ? 'reverse,' : '') +
49
+ "select,#{fragment.start},#{fragment_duration},\"#{fragment_outfile}\" " +
50
+ "-o:#{outfile} " +
51
+ "-y:#{position}"
52
+
53
+ position += fragment_duration
54
+ end
55
+
56
+ run_command(cmd.join(' '))
57
+ end
58
+
59
+ def mix_files(filenames, outfile)
60
+ cmd = %w/ecasound/
61
+
62
+ filenames.each_with_index do |tf, index|
63
+ cmd << "-a:#{index} -i:#{tf}"
64
+ end
65
+
66
+ cmd << "-a:all -o:#{outfile}"
67
+ run_command(cmd.join(' '))
68
+ end
69
+
70
+ def to_file(filename, options)
71
+ filename = Pathname.new(filename)
72
+
73
+ if @tracks.flatten.empty?
74
+ raise EmptyFragment
75
+ end
76
+
77
+ options = {
78
+ :overwrite => false
79
+ }.merge(options)
80
+
81
+ if filename.exist?
82
+ if options[:overwrite]
83
+ filename.unlink
84
+ else
85
+ raise FileExists
86
+ end
87
+ end
88
+
89
+ TempDir.create do |dir|
90
+ tmpdir = Pathname.new(dir)
91
+ tmpfiles = []
92
+
93
+ @tracks.each_with_index do |fragments, track_index|
94
+ tmpfiles << tmpfile = tmpdir + 'track_%s.wav' % track_index.to_s
95
+ fragments_to_file(fragments, tmpfile, tmpdir)
96
+ end
97
+
98
+ mix_files(tmpfiles, final_tmpfile = tmpdir + 'tmp.wav')
99
+
100
+ if filename.extname == '.wav'
101
+ File.rename(final_tmpfile, filename)
102
+ else
103
+ run_command("ffmpeg -i \"#{final_tmpfile}\" \"#{filename}\"")
104
+ end
105
+ end
106
+ end
107
+
108
+ def which(command)
109
+ run_command("which #{command}")
110
+ end
111
+
112
+ def run_command(cmd)
113
+ logger.debug("run_command: #{cmd}")
114
+
115
+ result = ''
116
+ status = Open4.popen4(cmd) do |pid, stdin, stdout, stderr|
117
+ logger.debug(stderr.read)
118
+ result = stdout.read
119
+ end
120
+
121
+ if status.exitstatus != 0
122
+ raise CommandFailed.new(cmd)
123
+ end
124
+
125
+ return result
126
+ end
127
+ end
128
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: youpy-scissor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.19
4
+ version: 0.0.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - youpy
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-04 00:00:00 -07:00
12
+ date: 2009-07-19 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -68,8 +68,10 @@ files:
68
68
  - lib/scissor
69
69
  - lib/scissor/chunk.rb
70
70
  - lib/scissor/fragment.rb
71
+ - lib/scissor/loggable.rb
71
72
  - lib/scissor/sequence.rb
72
73
  - lib/scissor/sound_file.rb
74
+ - lib/scissor/writer.rb
73
75
  - lib/scissor.rb
74
76
  - data/silence.mp3
75
77
  has_rdoc: true