waveform 0.0.2 → 0.0.3

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.md CHANGED
@@ -46,7 +46,8 @@ There are some nifty options you can supply to switch things up:
46
46
  There are also some less-nifty options:
47
47
 
48
48
  -q will generate your waveform without printing out a bunch of stuff.
49
- -h will prit out a help screen with all this info.
49
+ -h will print out a help screen with all this info.
50
+ -F will automatically overwrite destination file.
50
51
 
51
52
  Generating a small waveform "cut out" of a white background is pretty useful,
52
53
  then you can overlay it on a web-gradient on the website for your new startup
@@ -39,6 +39,11 @@ optparse = OptionParser.new do |o|
39
39
  options[:quiet] = true
40
40
  end
41
41
 
42
+ options[:force] = false
43
+ o.on("-F", "--force", "Force generationg of waveform if file exists") do
44
+ options[:force] = true
45
+ end
46
+
42
47
  o.on("-h", "--help", "Display this screen") do
43
48
  puts o
44
49
  exit
@@ -1,4 +1,5 @@
1
1
  require "ruby-audio"
2
+ require "tempfile"
2
3
 
3
4
  begin
4
5
  require "oily_png"
@@ -7,20 +8,21 @@ rescue LoadError
7
8
  end
8
9
 
9
10
  class Waveform
10
- VERSION = "0.0.2"
11
+ VERSION = "0.0.3"
11
12
 
12
13
  DefaultOptions = {
13
14
  :method => :peak,
14
15
  :width => 1800,
15
16
  :height => 280,
16
17
  :background_color => "#666666",
17
- :color => "#00ccff"
18
+ :color => "#00ccff",
19
+ :force => false
18
20
  }
19
21
 
20
22
  TransparencyMask = "#00ff00"
21
23
  TransparencyAlternate = "#ffff00" # in case the mask is the background color!
22
24
 
23
- attr_reader :audio
25
+ attr_reader :source
24
26
 
25
27
  # Scope these under Waveform so you can catch the ones generated by just this
26
28
  # class.
@@ -42,18 +44,30 @@ class Waveform
42
44
  # Waveform.new("mp3s/Kickstart My Heart.mp3")
43
45
  # Waveform.new("mp3s/Kickstart My Heart.mp3", $stdout)
44
46
  #
45
- def initialize(audio, log=nil)
46
- raise ArgumentError.new("No source audio filename given, must be an existing sound file.") unless audio
47
- raise RuntimeError.new("Source audio file '#{audio}' not found.") unless File.exist?(audio)
47
+ def initialize(source, log=nil)
48
+ raise ArgumentError.new("No source audio filename given, must be an existing sound file.") unless source
49
+ raise RuntimeError.new("Source audio file '#{source}' not found.") unless File.exist?(source)
48
50
 
49
51
  @log = Log.new(log)
50
-
51
- if File.extname(audio) != ".wav"
52
- @audio = audio.sub /(.+)\.(.+)/, "\\1.wav"
53
- raise RuntimeError.new("Unable to decode source '#{audio}' to WAV. Do you have ffmpeg installed with an appropriate decoder for your source file?") unless to_wav(audio, @audio)
52
+ @log.start!
53
+
54
+ # @source is the path to the given source file, whatever it may be
55
+ @source = source
56
+
57
+ # @audio is the path to the actual audio file we will process, always wav
58
+ if File.extname(source) == ".wav"
59
+ @audio = File.open(source, "rb")
54
60
  else
55
- @audio = audio
61
+ # This happens in initialize so you can generate multiple waveforms from
62
+ # the same audio without decoding multiple times
63
+ #
64
+ # Note that we're leaving it up to the ruby/system GC to clean up these
65
+ # tempfiles because someone may be generating multiple waveform images
66
+ # from a single audio source so we can't explicitly unlink the tempfile.
67
+ @audio = to_wav(source)
56
68
  end
69
+
70
+ raise RuntimeError.new("Unable to decode source \'#{@source}\' to WAV. Do you have ffmpeg installed with an appropriate decoder for your source file?") unless @audio
57
71
  end
58
72
 
59
73
  # Generate a Waveform image at the given filename with the given options.
@@ -85,21 +99,29 @@ class Waveform
85
99
  # color background to achieve a "cutout" effect).
86
100
  # Default is #00ccff (cyan-ish).
87
101
  #
102
+ # :force => Force generation of waveform, overwriting WAV or PNG file.
103
+ #
88
104
  # Example:
89
105
  # waveform = Waveform.new("mp3s/Kickstart My Heart.mp3")
90
- #
106
+ #
91
107
  # waveform.generate("waves/Kickstart My Heart.png")
92
108
  # waveform.generate("waves/Kickstart My Heart.png", :method => :rms)
93
109
  # waveform.generate("waves/Kickstart My Heart.png", :color => "#ff00ff")
94
- #
110
+ #
95
111
  def generate(filename, options={})
96
112
  raise ArgumentError.new("No destination filename given for waveform") unless filename
97
- raise RuntimeError.new("Destination file #{filename} exists") if File.exists?(filename)
113
+
114
+ if File.exists?(filename)
115
+ if options[:force]
116
+ @log.out("Output file #{filename} encountered. Removing.")
117
+ File.unlink(filename)
118
+ else
119
+ raise RuntimeError.new("Destination file #{filename} exists. Use --force if you want to automatically remove it.")
120
+ end
121
+ end
98
122
 
99
123
  options = DefaultOptions.merge(options)
100
-
101
- @log.start!
102
-
124
+
103
125
  # Frames gives the amplitudes for each channel, for our waveform we're
104
126
  # saying the "visual" amplitude is the average of the amplitude across all
105
127
  # the channels. This might be a little weird w/ the "peak" method if the
@@ -161,7 +183,7 @@ class Waveform
161
183
 
162
184
  frames = []
163
185
 
164
- RubyAudio::Sound.open(audio) do |snd|
186
+ RubyAudio::Sound.open(@audio.path) do |snd|
165
187
  frames_read = 0
166
188
  frames_per_sample = (snd.info.frames.to_f / width.to_f).to_i
167
189
  sample = RubyAudio::Buffer.new("float", frames_per_sample, snd.info.channels)
@@ -179,18 +201,17 @@ class Waveform
179
201
 
180
202
  private
181
203
 
182
- # Decode audio to a wav file, returns true if the decode succeeded or false
183
- # otherwise.
184
- def to_wav(src, dest)
185
- @log.start!
186
- @log.out("Decoding source audio '#{src}' to WAV...")
187
-
188
- raise RuntimeError.new("Destination WAV file '#{dest}' exists!") if File.exists?(dest)
204
+ # Decode given src file to a wav Tempfile. Returns the Tempfile if the decode
205
+ # succeeded, or false if the decode failed.
206
+ def to_wav(src, force=false)
207
+ wav = nil
189
208
 
190
- system %Q{ffmpeg -i "#{src}" -f wav "#{dest}" > /dev/null 2>&1}
191
- @log.done!
209
+ @log.timed("Decoding source audio '#{src}' to WAV...") do
210
+ wav = Tempfile.new(File.basename(src))
211
+ system %Q{ffmpeg -y -i "#{src}" -f wav "#{wav.path}" > /dev/null 2>&1}
212
+ end
192
213
 
193
- File.exists?(dest)
214
+ return wav.size == 0 ? false : wav
194
215
  end
195
216
 
196
217
  # Returns an array of the peak of each channel for the given collection of
@@ -115,5 +115,60 @@ class WaveformTest < Test::Unit::TestCase
115
115
  assert_equal ChunkyPNG::Color.from_hex("#00ff00"), image[0, 0]
116
116
  assert_equal ChunkyPNG::Color::TRANSPARENT, image[60, 120]
117
117
  end
118
+
119
+ # Not sure how to best test this as it's totally dependent on the ruby and
120
+ # system GC when the tempfiles are removed (as we're not explicitly
121
+ # unlinking them).
122
+ # should "use a tempfile when generating a temporary wav" do
123
+ # tempfiles = Dir[File.join(Dir.tmpdir(), "sample_mp3*")].size
124
+ # Waveform.new(fixture("sample_mp3.mp3")).generate(output("cleanup_temporary_wav.png"))
125
+ # assert_equal tempfiles + 1, Dir[File.join(Dir.tmpdir(), "sample_mp3*")].size
126
+ # end
127
+
128
+ should "not delete source wav file if one was given" do
129
+ assert File.exists?(fixture("sample.wav"))
130
+ Waveform.new(fixture("sample.wav")).generate(output("keep_source_wav.png"))
131
+ assert File.exists?(fixture("sample.wav"))
132
+ end
133
+
134
+ should "raise an error if unable to decode to wav" do
135
+ assert_raise(Waveform::RuntimeError) do
136
+ Waveform.new(fixture("sample.txt")).generate(output("shouldnt_exist.png"))
137
+ end
138
+ end
139
+
140
+ context "with existing PNG files" do
141
+ setup do
142
+ @existing = output("existing.png")
143
+ FileUtils.touch @existing
144
+ end
145
+
146
+ should "generate waveform if :force is true and PNG exists" do
147
+ @waveform.generate(@existing, :force => true)
148
+ end
149
+
150
+ should "raise an exception if PNG exists and :force is false" do
151
+ assert_raises Waveform::RuntimeError do
152
+ @waveform.generate(@existing, :force => false)
153
+ end
154
+ end
155
+ end
156
+
157
+ context "with existing WAV files" do
158
+ setup do
159
+ @existing = output("existing.wav")
160
+ FileUtils.touch @existing
161
+ end
162
+
163
+ should "generate waveform if :force is true and WAV exists" do
164
+ @waveform.generate(@existing, :force => true)
165
+ end
166
+
167
+ should "raise an exception if WAV exists and :force is false" do
168
+ assert_raises Waveform::RuntimeError do
169
+ @waveform.generate(@existing, :force => false)
170
+ end
171
+ end
172
+ end
118
173
  end
119
174
  end
@@ -3,8 +3,8 @@ require "./lib/waveform"
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "waveform"
5
5
  s.version = Waveform::VERSION
6
- s.summary = "Generate waveform images from WAV and MP3 files"
7
- s.description = "Generate waveform images from WAV and MP3 files -- in your code or via included CLI."
6
+ s.summary = "Generate waveform images from WAV, MP3, etc... files"
7
+ s.description = "Generate waveform images from WAV, MP3, etc... files - as a gem or via CLI."
8
8
  s.authors = ["Ben Alavi"]
9
9
  s.email = ["benalavi@gmail.com"]
10
10
  s.homepage = "http://github.com/benalavi/waveform"
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: waveform
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.2
5
+ version: 0.0.3
6
6
  platform: ruby
7
7
  authors:
8
8
  - Ben Alavi
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-07-18 00:00:00 Z
13
+ date: 2011-07-28 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: ruby-audio
@@ -45,7 +45,7 @@ dependencies:
45
45
  version: "0"
46
46
  type: :development
47
47
  version_requirements: *id003
48
- description: Generate waveform images from WAV and MP3 files -- in your code or via included CLI.
48
+ description: Generate waveform images from WAV, MP3, etc... files - as a gem or via CLI.
49
49
  email:
50
50
  - benalavi@gmail.com
51
51
  executables:
@@ -84,9 +84,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
84
  requirements: []
85
85
 
86
86
  rubyforge_project:
87
- rubygems_version: 1.8.5
87
+ rubygems_version: 1.8.6
88
88
  signing_key:
89
89
  specification_version: 3
90
- summary: Generate waveform images from WAV and MP3 files
90
+ summary: Generate waveform images from WAV, MP3, etc... files
91
91
  test_files: []
92
92