scissor 0.0.26 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -15,6 +15,7 @@ supported file format:
15
15
 
16
16
  * {FFmpeg}[http://ffmpeg.mplayerhq.hu/]
17
17
  * {Ecasound}[http://www.eca.cx/ecasound/] 2.5.0 or higher
18
+ * {Rubber Band}[http://breakfastquay.com/rubberband/]
18
19
 
19
20
  === Archive Installation
20
21
 
@@ -67,6 +68,10 @@ supported file format:
67
68
 
68
69
  foo.pitch(50)
69
70
 
71
+ === 200% time stretch without changing the pitch
72
+
73
+ foo.stretch(200)
74
+
70
75
  === mix
71
76
 
72
77
  Scissor.mix([foo, bar], 'mix.mp3')
data/Rakefile CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'rubygems'
2
+ gem 'rspec', '< 2'
2
3
  require 'rake'
3
4
  require 'rake/clean'
4
5
  require 'spec/rake/spectask'
@@ -17,7 +18,7 @@ DESCRIPTION = "utility to chop sound files"
17
18
  RUBYFORGE_PROJECT = "scissor"
18
19
  HOMEPATH = "http://github.com/youpy/scissor"
19
20
  BIN_FILES = %w( )
20
- VERS = "0.0.26"
21
+ VERS = "0.1.0"
21
22
 
22
23
  REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
23
24
  CLEAN.include ['**/.*.sw?', '*.gem', '.config']
data/lib/scissor/chunk.rb CHANGED
@@ -147,7 +147,7 @@ module Scissor
147
147
  new_instance.add_fragment(Fragment.new(
148
148
  fragment.filename,
149
149
  fragment.start,
150
- fragment.true_duration,
150
+ fragment.original_duration,
151
151
  !fragment.reversed?,
152
152
  fragment.pitch))
153
153
  end
@@ -155,21 +155,27 @@ module Scissor
155
155
  new_instance
156
156
  end
157
157
 
158
- def pitch(pitch)
158
+ def pitch(pitch, stretch = false)
159
159
  new_instance = self.class.new
160
160
 
161
161
  @fragments.each do |fragment|
162
162
  new_instance.add_fragment(Fragment.new(
163
163
  fragment.filename,
164
164
  fragment.start,
165
- fragment.true_duration,
165
+ fragment.original_duration,
166
166
  fragment.reversed?,
167
- fragment.pitch * (pitch.to_f / 100)))
167
+ fragment.pitch * (pitch.to_f / 100),
168
+ stretch))
168
169
  end
169
170
 
170
171
  new_instance
171
172
  end
172
173
 
174
+ def stretch(factor)
175
+ factor_for_pitch = ((100 / factor.to_f) * 100).to_i
176
+ pitch(factor_for_pitch, true)
177
+ end
178
+
173
179
  def to_file(filename, options = {})
174
180
  Scissor.mix([self], filename, options)
175
181
  end
@@ -4,12 +4,13 @@ module Scissor
4
4
  class Fragment
5
5
  attr_reader :filename, :start, :pitch
6
6
 
7
- def initialize(filename, start, duration, reverse = false, pitch = 100)
7
+ def initialize(filename, start, duration, reverse = false, pitch = 100, stretch = false)
8
8
  @filename = Pathname.new(filename).realpath
9
9
  @start = start
10
10
  @duration = duration
11
11
  @reverse = reverse
12
12
  @pitch = pitch
13
+ @is_stretched = stretch
13
14
 
14
15
  freeze
15
16
  end
@@ -18,7 +19,7 @@ module Scissor
18
19
  @duration * (100 / pitch.to_f)
19
20
  end
20
21
 
21
- def true_duration
22
+ def original_duration
22
23
  @duration
23
24
  end
24
25
 
@@ -26,6 +27,10 @@ module Scissor
26
27
  @reverse
27
28
  end
28
29
 
30
+ def stretched?
31
+ @is_stretched
32
+ end
33
+
29
34
  def create(remaining_start, remaining_length)
30
35
  new_fragment = nil
31
36
 
@@ -11,49 +11,65 @@ module Scissor
11
11
  class FileExists < Error; end
12
12
  class EmptyFragment < Error; end
13
13
  class CommandFailed < Error; end
14
+ class CommandNotFound < Error; end
14
15
 
15
16
  def initialize
16
17
  @tracks = []
17
18
 
18
19
  which('ecasound')
19
20
  which('ffmpeg')
21
+ which('rubberband')
20
22
  end
21
23
 
22
24
  def add_track(fragments)
23
25
  @tracks << fragments
24
26
  end
25
27
 
26
- def fragments_to_file(fragments, outfile, tmpdir)
28
+ def join_fragments(fragments, outfile, tmpdir)
27
29
  position = 0.0
28
30
  cmd = %w/ecasound/
29
31
 
30
32
  fragments.each_with_index do |fragment, index|
31
33
  fragment_filename = fragment.filename
32
- fragment_duration = fragment.duration
33
34
 
34
35
  if !index.zero? && (index % 28).zero?
35
36
  run_command(cmd.join(' '))
36
37
  cmd = %w/ecasound/
37
38
  end
38
39
 
39
- fragment_outfile =
40
- fragment_filename.extname.downcase == '.wav' ? fragment_filename :
41
- tmpdir + (Digest::MD5.hexdigest(fragment_filename.to_s) + '.wav')
40
+ if fragment_filename.extname.downcase == '.wav'
41
+ fragment_outfile = fragment_filename
42
+ else
43
+ fragment_outfile = tmpdir + (Digest::MD5.hexdigest(fragment_filename.to_s) + '.wav')
44
+ end
42
45
 
43
46
  unless fragment_outfile.exist?
44
47
  run_command("ffmpeg -i \"#{fragment_filename}\" \"#{fragment_outfile}\"")
45
48
  end
46
49
 
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}"
50
+ cmd << "-a:#{index} -o:#{outfile} -y:#{position}"
51
+
52
+ if fragment.stretched? && fragment.pitch.to_f != 100.0
53
+ rubberband_out = tmpdir + (Digest::MD5.hexdigest(fragment_filename.to_s) + "rubberband_#{index}.wav")
54
+ rubberband_temp = tmpdir + "_rubberband.wav"
55
+
56
+ run_command("ecasound " +
57
+ "-i:" +
58
+ (fragment.reversed? ? 'reverse,' : '') +
59
+ "select,#{fragment.start},#{fragment.original_duration},\"#{fragment_outfile}\" -o:#{rubberband_temp} "
60
+ )
61
+ run_command("rubberband -T #{fragment.pitch.to_f/100} \"#{rubberband_temp}\" \"#{rubberband_out}\"")
62
+
63
+ cmd << "-i:\"#{rubberband_out}\""
64
+ else
65
+ cmd <<
66
+ "-i:" +
67
+ (fragment.reversed? ? 'reverse,' : '') +
68
+ "select,#{fragment.start},#{fragment.original_duration},\"#{fragment_outfile}\" " +
69
+ (fragment.pitch.to_f == 100.0 ? "" : "-ei:#{fragment.pitch} ")
70
+ end
55
71
 
56
- position += fragment_duration
72
+ position += fragment.duration
57
73
  end
58
74
 
59
75
  run_command(cmd.join(' '))
@@ -96,8 +112,9 @@ module Scissor
96
112
  tmpfiles = []
97
113
 
98
114
  @tracks.each_with_index do |fragments, track_index|
99
- tmpfiles << tmpfile = tmpdir + 'track_%s.wav' % track_index.to_s
100
- fragments_to_file(fragments, tmpfile, tmpdir)
115
+ tmpfile = tmpdir + 'track_%s.wav' % track_index.to_s
116
+ tmpfiles << tmpfile
117
+ join_fragments(fragments, tmpfile, tmpdir)
101
118
  end
102
119
 
103
120
  mix_files(tmpfiles, final_tmpfile = tmpdir + 'tmp.wav')
@@ -112,6 +129,8 @@ module Scissor
112
129
 
113
130
  def which(command)
114
131
  run_command("which #{command}")
132
+ rescue
133
+ raise CommandNotFound.new(command + ': command not found')
115
134
  end
116
135
 
117
136
  def run_command(cmd)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scissor
3
3
  version: !ruby/object:Gem::Version
4
- hash: 43
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
+ - 1
8
9
  - 0
9
- - 26
10
- version: 0.0.26
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - youpy
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-06-18 00:00:00 +09:00
18
+ date: 2011-07-20 00:00:00 +09:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency