carrierwave-audio 1.0.2 → 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 +4 -4
- data/lib/carrierwave/audio.rb +18 -72
- data/lib/carrierwave/audio/processor.rb +226 -0
- data/lib/carrierwave/audio/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7be11dce3db2c4671534890d709b8c722552194b
|
|
4
|
+
data.tar.gz: 982706f2ba999761581f510862c7bb9da9a594db
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9aa03b8e0299a9c7862bda92ee5cee76919c0e57fe9a55643e7117c37ae99bc4313cbf8a0ac8c6fdb3aeb76ba87c45e708831fa516163d3303321198edb58b26
|
|
7
|
+
data.tar.gz: 6e69134f3fc3437b1cdca8eb2eb66ef1fe680d658a1a3afddb171845a079a04414bfc39b0e8f10e33017375f71ffe59eea15bb12a48491184cc427d9b8014827
|
data/lib/carrierwave/audio.rb
CHANGED
|
@@ -1,96 +1,42 @@
|
|
|
1
1
|
require 'carrierwave'
|
|
2
|
-
require '
|
|
3
|
-
require 'soxi/wrapper'
|
|
2
|
+
require 'carrierwave/audio/processor'
|
|
4
3
|
|
|
5
4
|
module CarrierWave
|
|
6
5
|
module Audio
|
|
7
6
|
module ClassMethods
|
|
8
7
|
extend ActiveSupport::Concern
|
|
9
8
|
|
|
10
|
-
def convert
|
|
11
|
-
process convert: [
|
|
9
|
+
def convert options={}
|
|
10
|
+
process convert: [ options ]
|
|
12
11
|
end
|
|
13
12
|
|
|
14
|
-
def watermark
|
|
15
|
-
process watermark: [
|
|
13
|
+
def watermark options={}
|
|
14
|
+
process watermark: [ options ]
|
|
16
15
|
end
|
|
17
16
|
end
|
|
18
17
|
|
|
19
|
-
def convert
|
|
18
|
+
def convert options={}
|
|
20
19
|
cache_stored_file! if !cached?
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
convert_file(current_path, input_options, tmp_path, default_output_options(format).merge(output_options))
|
|
27
|
-
File.rename tmp_path, current_path
|
|
28
|
-
set_content_type format
|
|
20
|
+
|
|
21
|
+
audio_filename = Processor.convert(current_path, options)
|
|
22
|
+
extension = File.extname(audio_filename)
|
|
23
|
+
File.rename audio_filename, current_path
|
|
24
|
+
set_content_type extension
|
|
29
25
|
end
|
|
30
26
|
|
|
31
|
-
def watermark
|
|
27
|
+
def watermark options={}
|
|
32
28
|
cache_stored_file! if !cached?
|
|
33
|
-
format = sanitized_format(output_format)
|
|
34
|
-
ext = File.extname(current_path)
|
|
35
|
-
watermark_ext = File.extname(watermark_file_path)
|
|
36
|
-
input_options = { type: ext.gsub(/\./, '') }
|
|
37
|
-
watermark_options = { type: watermark_ext.gsub(/\./, '') }
|
|
38
|
-
current_filename_without_extension = File.basename(current_path, ext)
|
|
39
|
-
|
|
40
|
-
# Normalize file to -6dB
|
|
41
|
-
normalized_tmp_path = File.join File.dirname(current_path), "tmp_norm_#{current_filename_without_extension}_#{Time.current.to_i}.#{input_options[:type]}"
|
|
42
|
-
convert_file(current_path, input_options, normalized_tmp_path, default_output_options(input_options[:type]), { gain: "-n -6" })
|
|
43
29
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
combiner.add_input watermark_file_path, watermark_options
|
|
49
|
-
combiner.set_output final_tmp_path, default_output_options(format).merge(output_options)
|
|
50
|
-
combiner.set_effects({ trim: "0 #{Soxi::Wrapper.file(current_path).seconds}", gain: "-n" })
|
|
51
|
-
combiner.run
|
|
52
|
-
File.rename final_tmp_path, current_path
|
|
53
|
-
set_content_type format
|
|
30
|
+
audio_filename = Processor.watermark(current_path, options)
|
|
31
|
+
extension = File.extname(audio_filename)
|
|
32
|
+
File.rename audio_filename, current_path
|
|
33
|
+
set_content_type extension
|
|
54
34
|
end
|
|
55
35
|
|
|
56
36
|
private
|
|
57
37
|
|
|
58
|
-
def
|
|
59
|
-
|
|
60
|
-
converter.add_input input_file_path, input_options
|
|
61
|
-
converter.set_output output_file_path, output_options
|
|
62
|
-
converter.set_effects fx
|
|
63
|
-
converter.run
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def sanitized_format format
|
|
67
|
-
supported_formats = [:mp3]
|
|
68
|
-
if supported_formats.include?(format.to_sym)
|
|
69
|
-
format.to_s
|
|
70
|
-
else
|
|
71
|
-
raise CarrierWave::ProcessingError.new("Unsupported audio format #{format}. Only conversion to #{supported_formats.to_sentence} allowed.")
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def default_output_options format
|
|
76
|
-
if format.to_sym == :mp3
|
|
77
|
-
{
|
|
78
|
-
type: format.to_s,
|
|
79
|
-
rate: 44100,
|
|
80
|
-
channels: 2,
|
|
81
|
-
compression: 128
|
|
82
|
-
}
|
|
83
|
-
else
|
|
84
|
-
{
|
|
85
|
-
type: format.to_s,
|
|
86
|
-
rate: 44100,
|
|
87
|
-
channels: 2
|
|
88
|
-
}
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def set_content_type format
|
|
93
|
-
case format.to_sym
|
|
38
|
+
def set_content_type extension
|
|
39
|
+
case extension.to_sym
|
|
94
40
|
when :mp3
|
|
95
41
|
self.file.instance_variable_set(:@content_type, "audio/mpeg3")
|
|
96
42
|
end
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
require 'ruby-sox'
|
|
2
|
+
require 'soxi/wrapper'
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
module CarrierWave
|
|
6
|
+
module Audio
|
|
7
|
+
class Processor
|
|
8
|
+
DefaultConvertOptions = {
|
|
9
|
+
output_format: :mp3,
|
|
10
|
+
logger: nil
|
|
11
|
+
}
|
|
12
|
+
DefaultWatermarkOptions = {
|
|
13
|
+
output_format: :mp3,
|
|
14
|
+
logger: nil
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# Scope these under Processor so you can catch the ones generated by just this
|
|
18
|
+
# class.
|
|
19
|
+
class RuntimeError < ::RuntimeError;end;
|
|
20
|
+
class ArgumentError < ::ArgumentError;end;
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
# Convert an audio file at the given filename with the given options.
|
|
24
|
+
#
|
|
25
|
+
# Available options (all optional) are:
|
|
26
|
+
#
|
|
27
|
+
# :output_format => Output file format
|
|
28
|
+
#
|
|
29
|
+
# Currently only :mp3 supported
|
|
30
|
+
# Default is :mp3.
|
|
31
|
+
#
|
|
32
|
+
# :logger => IOStream to log progress to.
|
|
33
|
+
#
|
|
34
|
+
# Example:
|
|
35
|
+
# CarrierWave::Audio::Processor.convert("Kickstart My Heart.wav")
|
|
36
|
+
# CarrierWave::Audio::Processor.generate("Kickstart My Heart.wav", :output_format => :wav)
|
|
37
|
+
#
|
|
38
|
+
def convert(source, options={})
|
|
39
|
+
options = DefaultConvertOptions.merge(options)
|
|
40
|
+
format = sanitized_format(options[:output_format])
|
|
41
|
+
|
|
42
|
+
@log = Log.new(options[:logger])
|
|
43
|
+
@log.start!
|
|
44
|
+
|
|
45
|
+
ext = File.extname(source)
|
|
46
|
+
input_options = { type: ext.gsub(/\./, '') }
|
|
47
|
+
final_filename = tmp_filename(source: source, format: format)
|
|
48
|
+
@log.timed("\nConverting...") do
|
|
49
|
+
convert_file(
|
|
50
|
+
input_file_path: source,
|
|
51
|
+
input_options: input_options,
|
|
52
|
+
output_file_path: final_filename,
|
|
53
|
+
output_options: output_options_for_format(format)
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
@log.done!("Converted audio file '#{final_filename}'")
|
|
58
|
+
|
|
59
|
+
final_filename
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Watermark an audio file at the given filename with the given options.
|
|
63
|
+
#
|
|
64
|
+
# Available options are:
|
|
65
|
+
#
|
|
66
|
+
# :watermark_file => (REQUIRED) Watermark audio file path
|
|
67
|
+
#
|
|
68
|
+
# :output_format => (Optional) Output file format
|
|
69
|
+
#
|
|
70
|
+
# Currently only :mp3 supported
|
|
71
|
+
# Default is :mp3.
|
|
72
|
+
#
|
|
73
|
+
# :logger => (Optional) IOStream to log progress to.
|
|
74
|
+
#
|
|
75
|
+
# Example:
|
|
76
|
+
# CarrierWave::Audio::Processor.watermark("Kickstart My Heart.wav", { watermark_file: "#{Rails.root}/db/watermark.mp3" })
|
|
77
|
+
#
|
|
78
|
+
def watermark(source, options={})
|
|
79
|
+
options = DefaultWatermarkOptions.merge(options)
|
|
80
|
+
format = sanitized_format(options[:output_format])
|
|
81
|
+
watermark_file_path = options[:watermark_file]
|
|
82
|
+
|
|
83
|
+
raise ArgumentError.new("No watermark filename given, must be a path to an existing sound file.") unless watermark_file_path
|
|
84
|
+
raise RuntimeError.new("Watermark file '#{watermark_file_path}' not found.") unless File.exist?(watermark_file_path)
|
|
85
|
+
|
|
86
|
+
@log = Log.new(options[:logger])
|
|
87
|
+
@log.start!
|
|
88
|
+
|
|
89
|
+
ext = File.extname(source)
|
|
90
|
+
input_options = { type: ext.gsub(/\./, '') }
|
|
91
|
+
normalized_filename = tmp_filename(source: source, format: format, prefix: "norm")
|
|
92
|
+
@log.timed("\nNormalizing file to -6dB...") do
|
|
93
|
+
convert_file(
|
|
94
|
+
input_file_path: source,
|
|
95
|
+
input_options: input_options,
|
|
96
|
+
output_file_path: normalized_filename,
|
|
97
|
+
output_options: output_options_for_format(input_options[:type]),
|
|
98
|
+
fx: { gain: "-n -6" }
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
watermark_ext = File.extname(watermark_file_path)
|
|
103
|
+
watermark_options = { type: watermark_ext.gsub(/\./, '') }
|
|
104
|
+
final_filename = tmp_filename(source: source, format: format, prefix: "wtmk")
|
|
105
|
+
@log.timed("\nCombining normalized file and watermark, normalizing final output to 0dB...") do
|
|
106
|
+
combiner = Sox::Cmd.new(combine: :mix)
|
|
107
|
+
combiner.add_input normalized_filename, input_options
|
|
108
|
+
combiner.add_input watermark_file_path, watermark_options
|
|
109
|
+
combiner.set_output final_filename, output_options_for_format(format)
|
|
110
|
+
combiner.set_effects({ trim: "0 =#{Soxi::Wrapper.file(source).seconds}", gain: "-n" })
|
|
111
|
+
combiner.run
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
@log.done!("Watermarked audio file '#{final_filename}'")
|
|
115
|
+
|
|
116
|
+
final_filename
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
# Convert file using options
|
|
122
|
+
def convert_file input_file_path:, input_options:, output_file_path:, output_options:, fx: {}
|
|
123
|
+
converter = Sox::Cmd.new
|
|
124
|
+
converter.add_input input_file_path, input_options
|
|
125
|
+
converter.set_output output_file_path, output_options
|
|
126
|
+
converter.set_effects fx
|
|
127
|
+
converter.run
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def sanitized_format format
|
|
131
|
+
supported_formats = [:mp3]
|
|
132
|
+
if supported_formats.include?(format.to_sym)
|
|
133
|
+
format.to_s
|
|
134
|
+
else
|
|
135
|
+
raise ArgumentError.new("Unsupported audio format #{format}. Only conversion to #{supported_formats.to_sentence} allowed.")
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def output_options_for_format format
|
|
140
|
+
shared_options = {
|
|
141
|
+
channels: 2
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if format.to_sym == :mp3
|
|
145
|
+
{
|
|
146
|
+
type: format.to_s,
|
|
147
|
+
rate: 44100,
|
|
148
|
+
compression: 128
|
|
149
|
+
}.merge(shared_options)
|
|
150
|
+
else
|
|
151
|
+
{
|
|
152
|
+
type: format.to_s,
|
|
153
|
+
rate: 44100
|
|
154
|
+
}.merge(shared_options)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Generate a temporary filename
|
|
159
|
+
def tmp_filename source:, format:, prefix: nil
|
|
160
|
+
ext = File.extname(source)
|
|
161
|
+
source_filename_without_ext = File.basename(source, ext)
|
|
162
|
+
File.join File.dirname(source), "tmp#{prefix.present? ? '_' + prefix : ''}_#{source_filename_without_ext}_#{Time.now.to_i}.#{format}"
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# A simple class for logging + benchmarking, nice to have good feedback on a
|
|
166
|
+
# long batch operation.
|
|
167
|
+
#
|
|
168
|
+
# There's probably 10,000,000 other bechmarking classes, but writing this was
|
|
169
|
+
# easier than using Google.
|
|
170
|
+
class Log
|
|
171
|
+
attr_accessor :io
|
|
172
|
+
|
|
173
|
+
def initialize(io=$stdout)
|
|
174
|
+
@io = io
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Prints the given message to the log
|
|
178
|
+
def out(msg)
|
|
179
|
+
io.print(msg) if io
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Prints the given message to the log followed by the most recent benchmark
|
|
183
|
+
# (note that it calls .end! which will stop the benchmark)
|
|
184
|
+
def done!(msg="")
|
|
185
|
+
out "#{msg} (#{self.end!}s)\n"
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Starts a new benchmark clock and returns the index of the new clock.
|
|
189
|
+
#
|
|
190
|
+
# If .start! is called again before .end! then the time returned will be
|
|
191
|
+
# the elapsed time from the next call to start!, and calling .end! again
|
|
192
|
+
# will return the time from *this* call to start! (that is, the clocks are
|
|
193
|
+
# LIFO)
|
|
194
|
+
def start!
|
|
195
|
+
(@benchmarks ||= []) << Time.now
|
|
196
|
+
@current = @benchmarks.size - 1
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Returns the elapsed time from the most recently started benchmark clock
|
|
200
|
+
# and ends the benchmark, so that a subsequent call to .end! will return
|
|
201
|
+
# the elapsed time from the previously started benchmark clock.
|
|
202
|
+
def end!
|
|
203
|
+
elapsed = (Time.now - @benchmarks[@current])
|
|
204
|
+
@current -= 1
|
|
205
|
+
elapsed
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Returns the elapsed time from the benchmark clock w/ the given index (as
|
|
209
|
+
# returned from when .start! was called).
|
|
210
|
+
def time?(index)
|
|
211
|
+
Time.now - @benchmarks[index]
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Benchmarks the given block, printing out the given message first (if
|
|
215
|
+
# given).
|
|
216
|
+
def timed(message=nil, &block)
|
|
217
|
+
start!
|
|
218
|
+
out(message) if message
|
|
219
|
+
yield
|
|
220
|
+
done!
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: carrierwave-audio
|
|
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: 2016-
|
|
11
|
+
date: 2016-11-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: carrierwave
|
|
@@ -91,6 +91,7 @@ files:
|
|
|
91
91
|
- README.md
|
|
92
92
|
- lib/carrierwave-audio.rb
|
|
93
93
|
- lib/carrierwave/audio.rb
|
|
94
|
+
- lib/carrierwave/audio/processor.rb
|
|
94
95
|
- lib/carrierwave/audio/version.rb
|
|
95
96
|
homepage: https://github.com/TrevorHinesley/carrierwave-audio
|
|
96
97
|
licenses:
|