waveform 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +2 -1
- data/bin/waveform +5 -0
- data/lib/waveform.rb +49 -28
- data/test/waveform_test.rb +55 -0
- data/waveform.gemspec +2 -2
- metadata +5 -5
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
|
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
|
data/bin/waveform
CHANGED
@@ -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
|
data/lib/waveform.rb
CHANGED
@@ -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.
|
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 :
|
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(
|
46
|
-
raise ArgumentError.new("No source audio filename given, must be an existing sound file.") unless
|
47
|
-
raise RuntimeError.new("Source audio file '#{
|
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
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
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
|
-
|
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
|
183
|
-
#
|
184
|
-
def to_wav(src,
|
185
|
-
|
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
|
-
|
191
|
-
|
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
|
-
|
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
|
data/test/waveform_test.rb
CHANGED
@@ -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
|
data/waveform.gemspec
CHANGED
@@ -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
|
7
|
-
s.description = "Generate waveform images from WAV
|
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.
|
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-
|
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
|
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.
|
87
|
+
rubygems_version: 1.8.6
|
88
88
|
signing_key:
|
89
89
|
specification_version: 3
|
90
|
-
summary: Generate waveform images from WAV
|
90
|
+
summary: Generate waveform images from WAV, MP3, etc... files
|
91
91
|
test_files: []
|
92
92
|
|