youpy-scissor 0.0.19 → 0.0.20

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