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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 996e33c9cb030bee7964d4db792e9f1895de8c65
4
- data.tar.gz: abb3d0ca6e0c6a1e0140a0376ebdb3eb3b89eedd
3
+ metadata.gz: 7be11dce3db2c4671534890d709b8c722552194b
4
+ data.tar.gz: 982706f2ba999761581f510862c7bb9da9a594db
5
5
  SHA512:
6
- metadata.gz: 7575f6521c1acdb52b41d6e1055b41ce08781ce029d1083230f28c7740161026c4147dccb2a1282dd711bbf5ab700c2893502562ba844d876a9b533fafe3dc4a
7
- data.tar.gz: eae7eddc4a4088da30e36897258e178c07a7196fcc9c955833f91f0a5b092ceb142642b397bc61d1627c5c6760d0d5c8302c2e9c31bbb4c02a7dcdec28c45fec
6
+ metadata.gz: 9aa03b8e0299a9c7862bda92ee5cee76919c0e57fe9a55643e7117c37ae99bc4313cbf8a0ac8c6fdb3aeb76ba87c45e708831fa516163d3303321198edb58b26
7
+ data.tar.gz: 6e69134f3fc3437b1cdca8eb2eb66ef1fe680d658a1a3afddb171845a079a04414bfc39b0e8f10e33017375f71ffe59eea15bb12a48491184cc427d9b8014827
@@ -1,96 +1,42 @@
1
1
  require 'carrierwave'
2
- require 'ruby-sox'
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 output_format = :mp3, output_options = {}
11
- process convert: [ output_format, output_options ]
9
+ def convert options={}
10
+ process convert: [ options ]
12
11
  end
13
12
 
14
- def watermark watermark_file_path, output_format = :mp3, output_options = {}
15
- process watermark: [ watermark_file_path, output_format, output_options ]
13
+ def watermark options={}
14
+ process watermark: [ options ]
16
15
  end
17
16
  end
18
17
 
19
- def convert output_format = :mp3, output_options = {}
18
+ def convert options={}
20
19
  cache_stored_file! if !cached?
21
- format = sanitized_format(output_format)
22
- ext = File.extname(current_path)
23
- input_options = { type: ext.gsub(/\./, '') }
24
- current_filename_without_extension = File.basename(current_path, ext)
25
- tmp_path = File.join File.dirname(current_path), "tmp_#{current_filename_without_extension}_#{Time.current.to_i}.#{format}"
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 watermark_file_path, output_format = :mp3, output_options = {}
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
- # Combine normalized file and watermark, normalizing final product to 0dB
45
- final_tmp_path = File.join File.dirname(current_path), "tmp_wtmk_#{current_filename_without_extension}_#{Time.current.to_i}.#{format}"
46
- combiner = Sox::Cmd.new(combine: :mix)
47
- combiner.add_input normalized_tmp_path, input_options
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 convert_file input_file_path, input_options, output_file_path, output_options, fx = {}
59
- converter = Sox::Cmd.new
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
@@ -1,5 +1,5 @@
1
1
  module CarrierWave
2
2
  module Audio
3
- VERSION = '1.0.2'
3
+ VERSION = '1.0.3'
4
4
  end
5
5
  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.2
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-10-20 00:00:00.000000000 Z
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: