diff --git a/app/services/waveform_service.rb b/app/services/waveform_service.rb index fc0ce6f93b..4100443a87 100644 --- a/app/services/waveform_service.rb +++ b/app/services/waveform_service.rb @@ -75,33 +75,37 @@ def empty_waveform(master_file) def get_normalized_peaks(uri) wave_io = get_wave_io(uri.to_s) - peaks = gather_peaks(wave_io) + peaks = [] + reader = nil + abs_max = 0 + begin + reader = WaveFile::Reader.new(wave_io) + reader.each_buffer(@samples_per_pixel) do |buffer| + sample_min, sample_max = buffer.samples.minmax + peaks << [sample_min, sample_max] + abs_max = [abs_max, sample_min.abs, sample_max.abs].max + end + rescue WaveFile::InvalidFormatError + # ffmpeg generated no wavefile data + end return [] if peaks.blank? - max_peak = peaks.flatten.map(&:abs).max res = 2**(@bit_res - 1) - factor = max_peak.zero? ? 1 : res / max_peak.to_f - peaks.map { |peak| peak.collect { |num| (num * factor).to_i } } + factor = abs_max.zero? ? 1 : res / abs_max.to_f + peaks.each do |peak| + peak[0] = (peak[0] * factor).to_i + peak[1] = (peak[1] * factor).to_i + end + peaks ensure - Process.wait(wave_io.pid) if wave_io&.pid + reader&.close + wave_io&.close end def get_wave_io(uri) headers = "-headers $'Referer: #{Rails.application.routes.url_helpers.root_url}\r\n'" if uri.starts_with? "http" normalized_uri = uri.starts_with?("file") ? Addressable::URI.unencode(uri) : uri timeout = 60000000 # Must be in microseconds. Current value = 1 minute. - cmd = "#{Settings.ffmpeg.path} #{headers} -rw_timeout #{timeout} -i '#{normalized_uri}' -f wav -ar 44100 - 2> /dev/null" + cmd = "#{Settings.ffmpeg.path} #{headers} -rw_timeout #{timeout} -i '#{normalized_uri}' -f wav -ar 44100 -ac 1 - 2> /dev/null" IO.popen(cmd) end - - def gather_peaks(wav_file) - peaks = [] - begin - WaveFile::Reader.new(wav_file).each_buffer(@samples_per_pixel) do |buffer| - peaks << [buffer.samples.flatten.min, buffer.samples.flatten.max] - end - rescue WaveFile::InvalidFormatError - # ffmpeg generated no wavefile data - end - peaks - end end diff --git a/spec/fixtures/meow.json b/spec/fixtures/meow.json index 468602d53d..9506de99d7 100644 --- a/spec/fixtures/meow.json +++ b/spec/fixtures/meow.json @@ -14,4 +14,4 @@ * --- END LICENSE_HEADER BLOCK --- */ -{"sample_rate":44100,"bits":8,"samples_per_pixel":1024,"length":54,"data":[-75,72,-113,106,-109,104,-117,112,-128,127,-122,114,-105,86,-81,76,-81,76,-93,88,-108,105,-112,108,-99,96,-91,91,-77,77,-63,70,-49,55,-39,45,-36,40,-36,40,-30,31,-30,29,-28,25,-24,16,-17,16,-10,8,-7,7,-4,4,-3,3,-2,2,-2,2,-2,2,-1,1,-2,2,-2,2,-2,2,-1,1,-1,1,-1,1,-1,1,-1,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]} +{"sample_rate":44100,"bits":8,"samples_per_pixel":1024,"length":54,"data":[-75, 72, -113, 106, -109, 104, -117, 112, -127, 128, -122, 114, -105, 87, -81, 75, -80, 76, -94, 88, -108, 105, -111, 108, -98, 96, -90, 87, -76, 76, -63, 70, -47, 53, -37, 44, -34, 38, -34, 39, -29, 31, -29, 27, -27, 25, -24, 16, -15, 13, -9, 8, -6, 5, -3, 4, -2, 3, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]} diff --git a/spec/fixtures/meow.mono.wav b/spec/fixtures/meow.mono.wav new file mode 100644 index 0000000000..3384815280 Binary files /dev/null and b/spec/fixtures/meow.mono.wav differ diff --git a/spec/services/waveform_service_spec.rb b/spec/services/waveform_service_spec.rb index 3d230993fe..c145cfb4a8 100644 --- a/spec/services/waveform_service_spec.rb +++ b/spec/services/waveform_service_spec.rb @@ -18,7 +18,7 @@ let(:service) { WaveformService.new } describe "get_waveform_json" do - let(:wav_path) { File.join(Rails.root, "spec/fixtures/meow.wav") } + let(:wav_path) { File.join(Rails.root, "spec/fixtures/meow.mono.wav") } it "should return waveform json from file" do allow(service).to receive(:get_wave_io).and_return(open(wav_path)) @@ -67,7 +67,7 @@ context "http file" do let(:uri) { "http://domain/to/video.mp4" } - let(:cmd) {"#{Settings.ffmpeg.path} -headers $'Referer: http://test.host/\r\n' -rw_timeout 60000000 -i '#{uri}' -f wav -ar 44100 - 2> /dev/null"} + let(:cmd) {"#{Settings.ffmpeg.path} -headers $'Referer: http://test.host/\r\n' -rw_timeout 60000000 -i '#{uri}' -f wav -ar 44100 -ac 1 - 2> /dev/null"} it "should call ffmpeg with headers" do service.send(:get_wave_io, uri) @@ -77,7 +77,7 @@ context "local file" do let(:uri) { "file:///path/to/video.mp4" } - let(:cmd) {"#{Settings.ffmpeg.path} -rw_timeout 60000000 -i '#{uri}' -f wav -ar 44100 - 2> /dev/null"} + let(:cmd) {"#{Settings.ffmpeg.path} -rw_timeout 60000000 -i '#{uri}' -f wav -ar 44100 -ac 1 - 2> /dev/null"} it "should call ffmpeg without headers" do service.send(:get_wave_io, uri) @@ -87,7 +87,7 @@ context 'with spaces in filename' do let(:uri) { 'file:///path/to/special%20video%20file.mp4' } let(:unencoded_uri) { 'file:///path/to/special video file.mp4' } - let(:cmd) {"#{Settings.ffmpeg.path} -rw_timeout 60000000 -i '#{unencoded_uri}' -f wav -ar 44100 - 2> /dev/null"} + let(:cmd) {"#{Settings.ffmpeg.path} -rw_timeout 60000000 -i '#{unencoded_uri}' -f wav -ar 44100 -ac 1 - 2> /dev/null"} it "should call ffmpeg without url encoding" do service.send(:get_wave_io, uri)