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