carrierwave-audio 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
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: