carrierwave-audio-waveform 1.0.0
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +35 -0
- data/lib/carrierwave-audio-waveform.rb +1 -0
- data/lib/carrierwave/audio_waveform.rb +21 -0
- data/lib/carrierwave/audio_waveform/version.rb +5 -0
- data/lib/carrierwave/audio_waveform/waveformer.rb +397 -0
- metadata +148 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b817a62eeaac43003254b3790fdc864faf0a0980
|
4
|
+
data.tar.gz: 84176297d0547e499d84bedef2e22585618e1e8e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c321c0dcaf6d14e6a8c6526c532eca40cb30ecf5690fae3896c1185d308344621974d310fff12cda3446ff210a8747c536de918fed7d13cacca7262e8851f49f
|
7
|
+
data.tar.gz: 42ce80dacf18c25b68f7732e5983f4547a505bd991c50b9b7eddcaf62fce51c549e200c2dd7af6b5e8b41fbdd592456a16733ffb4ee13f88d437e526a1490dd2
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Jiri Kolarik
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# CarrierWave::AudioWaveform
|
2
|
+
|
3
|
+
Generate waveform images from audio files within Carrierwave
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'carrierwave-audio-waveform'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install carrierwave-audio-waveform
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Include CarrierWave::AudioWaveform into your CarrierWave uploader class:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
class AudioUploader < CarrierWave::Uploader::Base
|
25
|
+
include CarrierWave::AudioWaveform
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
1. Fork it
|
32
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
33
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
34
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
35
|
+
5. Create new Pull Request
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'carrierwave/audio_waveform'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'carrierwave'
|
2
|
+
require 'carrierwave/audio_waveform/waveformer'
|
3
|
+
|
4
|
+
module CarrierWave
|
5
|
+
module AudioWaveform
|
6
|
+
module ClassMethods
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
def waveform options={}
|
10
|
+
process waveform: [ options ]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def waveform options={}
|
15
|
+
cache_stored_file! if !cached?
|
16
|
+
|
17
|
+
image_filename = Waveformer.generate(current_path, options)
|
18
|
+
File.rename image_filename, current_path
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,397 @@
|
|
1
|
+
require 'ruby-audio'
|
2
|
+
require 'ruby-sox'
|
3
|
+
require 'oily_png'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
module CarrierWave
|
7
|
+
module AudioWaveform
|
8
|
+
class Waveformer
|
9
|
+
DefaultOptions = {
|
10
|
+
:method => :peak,
|
11
|
+
:width => 1800,
|
12
|
+
:height => 280,
|
13
|
+
:background_color => "#666666",
|
14
|
+
:color => "#00ccff",
|
15
|
+
:logger => nil
|
16
|
+
}
|
17
|
+
|
18
|
+
TransparencyMask = "#00ff00"
|
19
|
+
TransparencyAlternate = "#ffff00" # in case the mask is the background color!
|
20
|
+
|
21
|
+
attr_reader :source
|
22
|
+
|
23
|
+
# Scope these under Waveform so you can catch the ones generated by just this
|
24
|
+
# class.
|
25
|
+
class RuntimeError < ::RuntimeError;end;
|
26
|
+
class ArgumentError < ::ArgumentError;end;
|
27
|
+
|
28
|
+
class << self
|
29
|
+
# Generate a Waveform image at the given filename with the given options.
|
30
|
+
#
|
31
|
+
# Available options (all optional) are:
|
32
|
+
#
|
33
|
+
# :method => The method used to read sample frames, available methods
|
34
|
+
# are peak and rms. peak is probably what you're used to seeing, it uses
|
35
|
+
# the maximum amplitude per sample to generate the waveform, so the
|
36
|
+
# waveform looks more dynamic. RMS gives a more fluid waveform and
|
37
|
+
# probably more accurately reflects what you hear, but isn't as
|
38
|
+
# pronounced (typically).
|
39
|
+
#
|
40
|
+
# Can be :rms or :peak
|
41
|
+
# Default is :peak.
|
42
|
+
#
|
43
|
+
# :width => The width (in pixels) of the final waveform image.
|
44
|
+
# Default is 1800.
|
45
|
+
#
|
46
|
+
# :height => The height (in pixels) of the final waveform image.
|
47
|
+
# Default is 280.
|
48
|
+
#
|
49
|
+
# :auto_width => msec per pixel. This will overwrite the width of the
|
50
|
+
# final waveform image depending on the length of the audio file.
|
51
|
+
# Example:
|
52
|
+
# 100 => 1 pixel per 100 msec; a one minute audio file will result in a width of 600 pixels
|
53
|
+
#
|
54
|
+
# :background_color => Hex code of the background color of the generated
|
55
|
+
# waveform image. Pass :transparent for transparent background.
|
56
|
+
# Default is #666666 (gray).
|
57
|
+
#
|
58
|
+
# :color => Hex code of the color to draw the waveform, or can pass
|
59
|
+
# :transparent to render the waveform transparent (use w/ a solid
|
60
|
+
# color background to achieve a "cutout" effect).
|
61
|
+
# Default is #00ccff (cyan-ish).
|
62
|
+
#
|
63
|
+
# :sample_width => Integer specifying the sample width. If this
|
64
|
+
# is specified, there will be gaps (minimum of 1px wide, as specified
|
65
|
+
# by :gap_width) between samples that are this wide in pixels.
|
66
|
+
# Default is nil
|
67
|
+
# Minimum is 1 (for anything other than nil)
|
68
|
+
#
|
69
|
+
# :gap_width => Integer specifying the gap width. If sample_width
|
70
|
+
# is specified, this will be the size of the gaps between samples in pixels.
|
71
|
+
# Default is nil
|
72
|
+
# Minimum is 1 (for anything other than nil, or when sample_width is present but gap_width is not)
|
73
|
+
#
|
74
|
+
# :logger => IOStream to log progress to.
|
75
|
+
#
|
76
|
+
# Example:
|
77
|
+
# CarrierWave::AudioWaveform::Waveformer.generate("Kickstart My Heart.wav")
|
78
|
+
# CarrierWave::AudioWaveform::Waveformer.generate("Kickstart My Heart.wav", :method => :rms)
|
79
|
+
# CarrierWave::AudioWaveform::Waveformer.generate("Kickstart My Heart.wav", :color => "#ff00ff", :logger => $stdout)
|
80
|
+
#
|
81
|
+
def generate(source, options={})
|
82
|
+
options = DefaultOptions.merge(options)
|
83
|
+
filename = options[:filename] || self.generate_png_filename(source)
|
84
|
+
|
85
|
+
raise ArgumentError.new("No source audio filename given, must be an existing sound file.") unless source
|
86
|
+
raise ArgumentError.new("No destination filename given for waveform") unless filename
|
87
|
+
raise RuntimeError.new("Source audio file '#{source}' not found.") unless File.exist?(source)
|
88
|
+
|
89
|
+
old_source = source
|
90
|
+
source = generate_wav_source(source)
|
91
|
+
|
92
|
+
@log = Log.new(options[:logger])
|
93
|
+
@log.start!
|
94
|
+
|
95
|
+
if options[:auto_width]
|
96
|
+
RubyAudio::Sound.open(source) do |audio|
|
97
|
+
options[:width] = (audio.info.length * 1000 / options[:auto_width].to_i).ceil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Frames gives the amplitudes for each channel, for our waveform we're
|
102
|
+
# saying the "visual" amplitude is the average of the amplitude across all
|
103
|
+
# the channels. This might be a little weird w/ the "peak" method if the
|
104
|
+
# frames are very wide (i.e. the image width is very small) -- I *think*
|
105
|
+
# the larger the frames are, the more "peaky" the waveform should get,
|
106
|
+
# perhaps to the point of inaccurately reflecting the actual sound.
|
107
|
+
samples = frames(source, options[:width], options[:method]).collect do |frame|
|
108
|
+
frame.inject(0.0) { |sum, peak| sum + peak } / frame.size
|
109
|
+
end
|
110
|
+
|
111
|
+
@log.timed("\nDrawing...") do
|
112
|
+
# Don't remove the file until we're sure the
|
113
|
+
# source was readable
|
114
|
+
if File.exists?(filename)
|
115
|
+
@log.out("Output file #{filename} encountered. Removing.")
|
116
|
+
File.unlink(filename)
|
117
|
+
end
|
118
|
+
|
119
|
+
image = draw samples, options
|
120
|
+
image.save filename
|
121
|
+
end
|
122
|
+
|
123
|
+
if source != old_source
|
124
|
+
@log.out("Removing temporary file at #{source}")
|
125
|
+
FileUtils.rm(source)
|
126
|
+
end
|
127
|
+
|
128
|
+
@log.done!("Generated waveform '#{filename}'")
|
129
|
+
|
130
|
+
filename
|
131
|
+
end
|
132
|
+
|
133
|
+
def generate_png_filename(source)
|
134
|
+
ext = File.extname(source)
|
135
|
+
source_file_path_without_extension = File.join File.dirname(source), File.basename(source, ext)
|
136
|
+
"#{source_file_path_without_extension}.png"
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
|
142
|
+
# Returns a wav file if one was not passed in, or the original if it was
|
143
|
+
def generate_wav_source(source)
|
144
|
+
ext = File.extname(source)
|
145
|
+
ext_gsubbed = ext.gsub(/\./, '')
|
146
|
+
|
147
|
+
if ext != ".wav"
|
148
|
+
input_options = { type: ext_gsubbed }
|
149
|
+
output_options = { type: "wav" }
|
150
|
+
source_filename_without_extension = File.basename(source, ext)
|
151
|
+
output_file_path = File.join File.dirname(source), "tmp_#{source_filename_without_extension}_#{Time.now.to_i}.wav"
|
152
|
+
converter = Sox::Cmd.new
|
153
|
+
converter.add_input source, input_options
|
154
|
+
converter.set_output output_file_path, output_options
|
155
|
+
converter.run
|
156
|
+
output_file_path
|
157
|
+
else
|
158
|
+
source
|
159
|
+
end
|
160
|
+
rescue Sox::Error => e
|
161
|
+
raise e unless e.message.include?("FAIL formats:")
|
162
|
+
raise RuntimeError.new("Source file #{source} could not be converted to .wav by Sox (Sox: #{e.message})")
|
163
|
+
end
|
164
|
+
|
165
|
+
# Returns a sampling of frames from the given RubyAudio::Sound using the
|
166
|
+
# given method the sample size is determined by the given pixel width --
|
167
|
+
# we want one sample frame per horizontal pixel.
|
168
|
+
def frames(source, width, method = :peak)
|
169
|
+
raise ArgumentError.new("Unknown sampling method #{method}") unless [ :peak, :rms ].include?(method)
|
170
|
+
|
171
|
+
frames = []
|
172
|
+
|
173
|
+
RubyAudio::Sound.open(source) do |audio|
|
174
|
+
frames_read = 0
|
175
|
+
frames_per_sample = (audio.info.frames.to_f / width.to_f).to_i
|
176
|
+
sample = RubyAudio::Buffer.new("float", frames_per_sample, audio.info.channels)
|
177
|
+
|
178
|
+
@log.timed("Sampling #{frames_per_sample} frames per sample: ") do
|
179
|
+
while(frames_read = audio.read(sample)) > 0
|
180
|
+
frames << send(method, sample, audio.info.channels)
|
181
|
+
@log.out(".")
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
frames
|
187
|
+
rescue RubyAudio::Error => e
|
188
|
+
raise e unless e.message == "File contains data in an unknown format."
|
189
|
+
raise RuntimeError.new("Source audio file #{source} could not be read by RubyAudio library -- Hint: non-WAV files are no longer supported, convert to WAV first using something like ffmpeg (RubyAudio: #{e.message})")
|
190
|
+
end
|
191
|
+
|
192
|
+
# Draws the given samples using the given options, returns a ChunkyPNG::Image.
|
193
|
+
def draw(samples, options)
|
194
|
+
image = ChunkyPNG::Image.new(options[:width], options[:height],
|
195
|
+
options[:background_color] == :transparent ? ChunkyPNG::Color::TRANSPARENT : options[:background_color]
|
196
|
+
)
|
197
|
+
|
198
|
+
if options[:color] == :transparent
|
199
|
+
color = transparent = ChunkyPNG::Color.from_hex(
|
200
|
+
# Have to do this little bit because it's possible the color we were
|
201
|
+
# intending to use a transparency mask *is* the background color, and
|
202
|
+
# then we'd end up wiping out the whole image.
|
203
|
+
options[:background_color].downcase == TransparencyMask ? TransparencyAlternate : TransparencyMask
|
204
|
+
)
|
205
|
+
else
|
206
|
+
color = ChunkyPNG::Color.from_hex(options[:color])
|
207
|
+
end
|
208
|
+
|
209
|
+
# Calling "zero" the middle of the waveform, like there's positive and
|
210
|
+
# negative amplitude
|
211
|
+
zero = options[:height] / 2.0
|
212
|
+
|
213
|
+
# If a sample_width is passed, let's space those things out
|
214
|
+
if options[:sample_width]
|
215
|
+
samples = spaced_samples(samples, options[:sample_width], options[:gap_width])
|
216
|
+
end
|
217
|
+
|
218
|
+
samples.each_with_index do |sample, x|
|
219
|
+
next if sample.nil?
|
220
|
+
# Half the amplitude goes above zero, half below
|
221
|
+
amplitude = sample * options[:height].to_f / 2.0
|
222
|
+
# If you give ChunkyPNG floats for pixel positions all sorts of things
|
223
|
+
# go haywire.
|
224
|
+
image.line(x, (zero - amplitude).round, x, (zero + amplitude).round, color)
|
225
|
+
end
|
226
|
+
|
227
|
+
# Simple transparency masking, it just loops over every pixel and makes
|
228
|
+
# ones which match the transparency mask color completely clear.
|
229
|
+
if transparent
|
230
|
+
(0..image.width - 1).each do |x|
|
231
|
+
(0..image.height - 1).each do |y|
|
232
|
+
image[x, y] = ChunkyPNG::Color.rgba(0, 0, 0, 0) if image[x, y] == transparent
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
image
|
238
|
+
end
|
239
|
+
|
240
|
+
def spaced_samples samples, sample_width = 1, gap_width = 1
|
241
|
+
sample_width = sample_width.to_i >= 1 ? sample_width.to_i : 1
|
242
|
+
gap_width = gap_width.to_i >= 1 ? gap_width.to_i : 1
|
243
|
+
width_counter = sample_width
|
244
|
+
current_sample_index = 0
|
245
|
+
spaced_samples = []
|
246
|
+
avg = nil
|
247
|
+
while samples[current_sample_index]
|
248
|
+
at_front_of_image = current_sample_index < sample_width
|
249
|
+
|
250
|
+
# This determines if it's a gap, but we don't want
|
251
|
+
# a gap to start with, hence the last booelan check
|
252
|
+
if width_counter.to_i > sample_width.to_i && !at_front_of_image
|
253
|
+
# This is a gap
|
254
|
+
spaced_samples << nil
|
255
|
+
width_counter -= 1
|
256
|
+
else
|
257
|
+
# This is a sample
|
258
|
+
# If this is a new block of samples, get the average
|
259
|
+
if avg.nil?
|
260
|
+
avg = calculate_avg_sample(samples, current_sample_index, sample_width)
|
261
|
+
end
|
262
|
+
spaced_samples << avg
|
263
|
+
# This is 1-indexed since it starts at sample_width
|
264
|
+
# (or sample_width + gap_width for anything other than the initial passes)
|
265
|
+
if width_counter.to_i < 2
|
266
|
+
width_counter = sample_width + gap_width
|
267
|
+
avg = nil
|
268
|
+
else
|
269
|
+
width_counter -= 1
|
270
|
+
end
|
271
|
+
end
|
272
|
+
current_sample_index += 1
|
273
|
+
end
|
274
|
+
|
275
|
+
spaced_samples
|
276
|
+
end
|
277
|
+
|
278
|
+
# Calculate the average of a group of samples
|
279
|
+
# Return the sample's value if it's a group of 1
|
280
|
+
def calculate_avg_sample(samples, current_sample_index, sample_width)
|
281
|
+
if sample_width > 1
|
282
|
+
floats = samples[current_sample_index..(current_sample_index + sample_width - 1)].collect(&:to_f)
|
283
|
+
#floats.inject(:+) / sample_width
|
284
|
+
channel_rms(floats)
|
285
|
+
else
|
286
|
+
samples[current_sample_index]
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
# Returns an array of the peak of each channel for the given collection of
|
291
|
+
# frames -- the peak is individual to the channel, and the returned collection
|
292
|
+
# of peaks are not (necessarily) from the same frame(s).
|
293
|
+
def peak(frames, channels=1)
|
294
|
+
peak_frame = []
|
295
|
+
(0..channels-1).each do |channel|
|
296
|
+
peak_frame << channel_peak(frames, channel)
|
297
|
+
end
|
298
|
+
peak_frame
|
299
|
+
end
|
300
|
+
|
301
|
+
# Returns an array of rms values for the given frameset where each rms value is
|
302
|
+
# the rms value for that channel.
|
303
|
+
def rms(frames, channels=1)
|
304
|
+
rms_frame = []
|
305
|
+
(0..channels-1).each do |channel|
|
306
|
+
rms_frame << channel_rms(frames, channel)
|
307
|
+
end
|
308
|
+
rms_frame
|
309
|
+
end
|
310
|
+
|
311
|
+
# Returns the peak voltage reached on the given channel in the given collection
|
312
|
+
# of frames.
|
313
|
+
#
|
314
|
+
# TODO: Could lose some resolution and only sample every other frame, would
|
315
|
+
# likely still generate the same waveform as the waveform is so comparitively
|
316
|
+
# low resolution to the original input (in most cases), and would increase
|
317
|
+
# the analyzation speed (maybe).
|
318
|
+
def channel_peak(frames, channel=0)
|
319
|
+
peak = 0.0
|
320
|
+
frames.each do |frame|
|
321
|
+
next if frame.nil?
|
322
|
+
frame = Array(frame)
|
323
|
+
peak = frame[channel].abs if frame[channel].abs > peak
|
324
|
+
end
|
325
|
+
peak
|
326
|
+
end
|
327
|
+
|
328
|
+
# Returns the rms value across the given collection of frames for the given
|
329
|
+
# channel.
|
330
|
+
def channel_rms(frames, channel=0)
|
331
|
+
Math.sqrt(frames.inject(0.0){ |sum, frame| sum += (frame ? Array(frame)[channel] ** 2 : 0) } / frames.size)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
class Waveformer
|
337
|
+
# A simple class for logging + benchmarking, nice to have good feedback on a
|
338
|
+
# long batch operation.
|
339
|
+
#
|
340
|
+
# There's probably 10,000,000 other bechmarking classes, but writing this was
|
341
|
+
# easier than using Google.
|
342
|
+
class Log
|
343
|
+
attr_accessor :io
|
344
|
+
|
345
|
+
def initialize(io=$stdout)
|
346
|
+
@io = io
|
347
|
+
end
|
348
|
+
|
349
|
+
# Prints the given message to the log
|
350
|
+
def out(msg)
|
351
|
+
io.print(msg) if io
|
352
|
+
end
|
353
|
+
|
354
|
+
# Prints the given message to the log followed by the most recent benchmark
|
355
|
+
# (note that it calls .end! which will stop the benchmark)
|
356
|
+
def done!(msg="")
|
357
|
+
out "#{msg} (#{self.end!}s)\n"
|
358
|
+
end
|
359
|
+
|
360
|
+
# Starts a new benchmark clock and returns the index of the new clock.
|
361
|
+
#
|
362
|
+
# If .start! is called again before .end! then the time returned will be
|
363
|
+
# the elapsed time from the next call to start!, and calling .end! again
|
364
|
+
# will return the time from *this* call to start! (that is, the clocks are
|
365
|
+
# LIFO)
|
366
|
+
def start!
|
367
|
+
(@benchmarks ||= []) << Time.now
|
368
|
+
@current = @benchmarks.size - 1
|
369
|
+
end
|
370
|
+
|
371
|
+
# Returns the elapsed time from the most recently started benchmark clock
|
372
|
+
# and ends the benchmark, so that a subsequent call to .end! will return
|
373
|
+
# the elapsed time from the previously started benchmark clock.
|
374
|
+
def end!
|
375
|
+
elapsed = (Time.now - @benchmarks[@current])
|
376
|
+
@current -= 1
|
377
|
+
elapsed
|
378
|
+
end
|
379
|
+
|
380
|
+
# Returns the elapsed time from the benchmark clock w/ the given index (as
|
381
|
+
# returned from when .start! was called).
|
382
|
+
def time?(index)
|
383
|
+
Time.now - @benchmarks[index]
|
384
|
+
end
|
385
|
+
|
386
|
+
# Benchmarks the given block, printing out the given message first (if
|
387
|
+
# given).
|
388
|
+
def timed(message=nil, &block)
|
389
|
+
start!
|
390
|
+
out(message) if message
|
391
|
+
yield
|
392
|
+
done!
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
metadata
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: carrierwave-audio-waveform
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Trevor Hinesley
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: carrierwave
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ruby-audio
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: ruby-sox
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: oily_png
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: byebug
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: CarrierWave Audio Waveform
|
112
|
+
email:
|
113
|
+
- trevor@trevorhinesley.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- LICENSE.txt
|
119
|
+
- README.md
|
120
|
+
- lib/carrierwave-audio-waveform.rb
|
121
|
+
- lib/carrierwave/audio_waveform.rb
|
122
|
+
- lib/carrierwave/audio_waveform/version.rb
|
123
|
+
- lib/carrierwave/audio_waveform/waveformer.rb
|
124
|
+
homepage: https://github.com/TrevorHinesley/carrierwave-audio-waveform
|
125
|
+
licenses:
|
126
|
+
- MIT
|
127
|
+
metadata: {}
|
128
|
+
post_install_message:
|
129
|
+
rdoc_options: []
|
130
|
+
require_paths:
|
131
|
+
- lib
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
requirements: []
|
143
|
+
rubyforge_project:
|
144
|
+
rubygems_version: 2.5.1
|
145
|
+
signing_key:
|
146
|
+
specification_version: 4
|
147
|
+
summary: Generate waveform images from audio files within Carrierwave
|
148
|
+
test_files: []
|