carrierwave-audio-waveform 1.0.0 → 1.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.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b978937b699c8840ed808da400cc75406b850c7
|
4
|
+
data.tar.gz: e8ea1146faf2a37a1fcdb7c98172c1a241008bfc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 763f2a8d2b40333712f256cf8f8cac5e2a75559a30d24857a3075f1f8cb30f5ee1ac825b97d719b8d7bf46f09542fcf1432ba67f2d34a327a1ccf75062e5ff42
|
7
|
+
data.tar.gz: 377f117014e72cc62fb6c3dafb1a1882a77ce43100b548c10690da7c2f6f79c6564b5f80ca95c4f34503f6df2cf51f1afbb718e30ea7d8c956e2b12de2a116c7
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'carrierwave'
|
2
2
|
require 'carrierwave/audio_waveform/waveformer'
|
3
|
+
require 'carrierwave/audio_waveform/waveform_data'
|
3
4
|
|
4
5
|
module CarrierWave
|
5
6
|
module AudioWaveform
|
@@ -9,6 +10,10 @@ module CarrierWave
|
|
9
10
|
def waveform options={}
|
10
11
|
process waveform: [ options ]
|
11
12
|
end
|
13
|
+
|
14
|
+
def waveform_data options={}
|
15
|
+
process waveform_data: [ options ]
|
16
|
+
end
|
12
17
|
end
|
13
18
|
|
14
19
|
def waveform options={}
|
@@ -16,6 +21,15 @@ module CarrierWave
|
|
16
21
|
|
17
22
|
image_filename = Waveformer.generate(current_path, options)
|
18
23
|
File.rename image_filename, current_path
|
24
|
+
self.file.instance_variable_set(:@content_type, "image/png")
|
25
|
+
end
|
26
|
+
|
27
|
+
def waveform_data options={}
|
28
|
+
cache_stored_file! if !cached?
|
29
|
+
|
30
|
+
data_filename = WaveformData.generate(current_path, options)
|
31
|
+
File.rename data_filename, current_path
|
32
|
+
self.file.instance_variable_set(:@content_type, "application/json")
|
19
33
|
end
|
20
34
|
end
|
21
|
-
end
|
35
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'ruby-sox'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module CarrierWave
|
5
|
+
module AudioWaveform
|
6
|
+
class WaveformData
|
7
|
+
DefaultOptions = {
|
8
|
+
pixels_per_second: 10,
|
9
|
+
bits: 16
|
10
|
+
}
|
11
|
+
|
12
|
+
# Scope these under Waveform so you can catch the ones generated by just this
|
13
|
+
# class.
|
14
|
+
class RuntimeError < ::RuntimeError;end;
|
15
|
+
class ArgumentError < ::ArgumentError;end;
|
16
|
+
|
17
|
+
class << self
|
18
|
+
# Generate a Waveform image at the given filename with the given options.
|
19
|
+
#
|
20
|
+
# Available options (all optional) are:
|
21
|
+
#
|
22
|
+
# :convert_to_extension_before_processing => Symbolized extension (:wav, :mp3, etc.)
|
23
|
+
# Useful if .wav or .mp3 isn't being passed in--you can convert to that format first.
|
24
|
+
#
|
25
|
+
# :set_extension_before_processing => Symbolized extension (:wav, :mp3, etc.)
|
26
|
+
# This is useful because CarrierWave will send files in with the wrong extension sometimes.
|
27
|
+
# For instance, if this is nested under a version, that version may be an .mp3, but its parent
|
28
|
+
#
|
29
|
+
# :pixels_per_second => The number of pixels per second to evaluate.
|
30
|
+
#
|
31
|
+
# :bits => 8 or 16 bit precision
|
32
|
+
#
|
33
|
+
# :logger => IOStream to log progress to.
|
34
|
+
#
|
35
|
+
# Example:
|
36
|
+
# CarrierWave::AudioWaveform::Waveformer.generate("Kickstart My Heart.wav")
|
37
|
+
# CarrierWave::AudioWaveform::Waveformer.generate("Kickstart My Heart.wav", :method => :rms)
|
38
|
+
# CarrierWave::AudioWaveform::Waveformer.generate("Kickstart My Heart.wav", :color => "#ff00ff", :logger => $stdout)
|
39
|
+
#
|
40
|
+
def generate(source, options={})
|
41
|
+
options = DefaultOptions.merge(options)
|
42
|
+
options[:filename] ||= self.generate_json_filename(source)
|
43
|
+
old_source = source
|
44
|
+
if options[:convert_to_extension_before_processing]
|
45
|
+
source = generate_valid_source(source, options[:convert_to_extension_before_processing])
|
46
|
+
elsif options[:set_extension_before_processing]
|
47
|
+
source = generate_proper_source(source, options[:set_extension_before_processing])
|
48
|
+
end
|
49
|
+
|
50
|
+
raise ArgumentError.new("No source audio filename given, must be an existing sound file.") unless source
|
51
|
+
raise ArgumentError.new("No destination filename given for waveform") unless options[:filename]
|
52
|
+
raise RuntimeError.new("Source audio file '#{source}' not found.") unless File.exist?(source)
|
53
|
+
|
54
|
+
@log = Log.new(options[:logger])
|
55
|
+
@log.start!
|
56
|
+
|
57
|
+
@log.timed("\nGenerating...") do
|
58
|
+
stdout_str, stderr_str, status = self.generate_waveform_data(source, options)
|
59
|
+
if stderr_str.present? && !stderr_str.include?("Recoverable")
|
60
|
+
raise RuntimeError.new(stderr_str)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
if source != old_source && options[:convert_to_extension_before_processing]
|
65
|
+
@log.out("Removing temporary file at #{source}")
|
66
|
+
FileUtils.rm(source)
|
67
|
+
elsif source != old_source && options[:set_extension_before_processing]
|
68
|
+
@log.out("Renaming file at #{source}")
|
69
|
+
old_ext = File.extname(source).gsub(/\./, '').to_sym
|
70
|
+
generate_proper_source(source, old_ext)
|
71
|
+
end
|
72
|
+
|
73
|
+
@log.done!("Generated waveform data '#{options[:filename]}'")
|
74
|
+
|
75
|
+
options[:filename]
|
76
|
+
end
|
77
|
+
|
78
|
+
def generate_json_filename(source)
|
79
|
+
ext = File.extname(source)
|
80
|
+
source_file_path_without_extension = File.join File.dirname(source), File.basename(source, ext)
|
81
|
+
"#{source_file_path_without_extension}.json"
|
82
|
+
end
|
83
|
+
|
84
|
+
def generate_waveform_data(source, options = DefaultOptions)
|
85
|
+
options[:filename] ||= self.generate_json_filename(source)
|
86
|
+
Open3.capture3(
|
87
|
+
"audiowaveform -i #{source} --pixels-per-second #{options[:pixels_per_second]} -b #{options[:bits]} -o #{options[:filename]}"
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
# Returns the proper file type if the one passed in was
|
94
|
+
# wrong, or the original if it wasn't.
|
95
|
+
def generate_proper_source(source, proper_ext)
|
96
|
+
ext = File.extname(source)
|
97
|
+
ext_gsubbed = ext.gsub(/\./, '')
|
98
|
+
|
99
|
+
if ext_gsubbed != proper_ext.to_s
|
100
|
+
filename_with_proper_extension = "#{source.chomp(File.extname(source))}.#{proper_ext}"
|
101
|
+
File.rename source, filename_with_proper_extension
|
102
|
+
filename_with_proper_extension
|
103
|
+
else
|
104
|
+
source
|
105
|
+
end
|
106
|
+
rescue Sox::Error => e
|
107
|
+
raise e unless e.message.include?("FAIL formats:")
|
108
|
+
raise RuntimeError.new("Source file #{source} could not be converted to .wav by Sox (Sox: #{e.message})")
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns a converted file.
|
112
|
+
def generate_valid_source(source, proper_ext)
|
113
|
+
ext = File.extname(source)
|
114
|
+
ext_gsubbed = ext.gsub(/\./, '')
|
115
|
+
|
116
|
+
if ext_gsubbed != proper_ext.to_s
|
117
|
+
input_options = { type: ext_gsubbed }
|
118
|
+
output_options = { type: proper_ext.to_s }
|
119
|
+
source_filename_without_extension = File.basename(source, ext)
|
120
|
+
output_file_path = File.join File.dirname(source), "tmp_#{source_filename_without_extension}_#{Time.now.to_i}.#{proper_ext}"
|
121
|
+
converter = Sox::Cmd.new
|
122
|
+
converter.add_input source, input_options
|
123
|
+
converter.set_output output_file_path, output_options
|
124
|
+
converter.run
|
125
|
+
output_file_path
|
126
|
+
else
|
127
|
+
source
|
128
|
+
end
|
129
|
+
rescue Sox::Error => e
|
130
|
+
raise e unless e.message.include?("FAIL formats:")
|
131
|
+
raise RuntimeError.new("Source file #{source} could not be converted to .wav by Sox (Sox: #{e.message})")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class WaveformData
|
137
|
+
# A simple class for logging + benchmarking, nice to have good feedback on a
|
138
|
+
# long batch operation.
|
139
|
+
#
|
140
|
+
# There's probably 10,000,000 other bechmarking classes, but writing this was
|
141
|
+
# easier than using Google.
|
142
|
+
class Log
|
143
|
+
attr_accessor :io
|
144
|
+
|
145
|
+
def initialize(io=$stdout)
|
146
|
+
@io = io
|
147
|
+
end
|
148
|
+
|
149
|
+
# Prints the given message to the log
|
150
|
+
def out(msg)
|
151
|
+
io.print(msg) if io
|
152
|
+
end
|
153
|
+
|
154
|
+
# Prints the given message to the log followed by the most recent benchmark
|
155
|
+
# (note that it calls .end! which will stop the benchmark)
|
156
|
+
def done!(msg="")
|
157
|
+
out "#{msg} (#{self.end!}s)\n"
|
158
|
+
end
|
159
|
+
|
160
|
+
# Starts a new benchmark clock and returns the index of the new clock.
|
161
|
+
#
|
162
|
+
# If .start! is called again before .end! then the time returned will be
|
163
|
+
# the elapsed time from the next call to start!, and calling .end! again
|
164
|
+
# will return the time from *this* call to start! (that is, the clocks are
|
165
|
+
# LIFO)
|
166
|
+
def start!
|
167
|
+
(@benchmarks ||= []) << Time.now
|
168
|
+
@current = @benchmarks.size - 1
|
169
|
+
end
|
170
|
+
|
171
|
+
# Returns the elapsed time from the most recently started benchmark clock
|
172
|
+
# and ends the benchmark, so that a subsequent call to .end! will return
|
173
|
+
# the elapsed time from the previously started benchmark clock.
|
174
|
+
def end!
|
175
|
+
elapsed = (Time.now - @benchmarks[@current])
|
176
|
+
@current -= 1
|
177
|
+
elapsed
|
178
|
+
end
|
179
|
+
|
180
|
+
# Returns the elapsed time from the benchmark clock w/ the given index (as
|
181
|
+
# returned from when .start! was called).
|
182
|
+
def time?(index)
|
183
|
+
Time.now - @benchmarks[index]
|
184
|
+
end
|
185
|
+
|
186
|
+
# Benchmarks the given block, printing out the given message first (if
|
187
|
+
# given).
|
188
|
+
def timed(message=nil, &block)
|
189
|
+
start!
|
190
|
+
out(message) if message
|
191
|
+
yield
|
192
|
+
done!
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: carrierwave-audio-waveform
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Trevor Hinesley
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: carrierwave
|
@@ -120,6 +120,7 @@ files:
|
|
120
120
|
- lib/carrierwave-audio-waveform.rb
|
121
121
|
- lib/carrierwave/audio_waveform.rb
|
122
122
|
- lib/carrierwave/audio_waveform/version.rb
|
123
|
+
- lib/carrierwave/audio_waveform/waveform_data.rb
|
123
124
|
- lib/carrierwave/audio_waveform/waveformer.rb
|
124
125
|
homepage: https://github.com/TrevorHinesley/carrierwave-audio-waveform
|
125
126
|
licenses:
|
@@ -146,3 +147,4 @@ signing_key:
|
|
146
147
|
specification_version: 4
|
147
148
|
summary: Generate waveform images from audio files within Carrierwave
|
148
149
|
test_files: []
|
150
|
+
has_rdoc:
|