Skip to content

Commit

Permalink
File creating and caching now works for mp3 and spectrograms.
Browse files Browse the repository at this point in the history
modified:   Gemfile.lock
modified:   app/controllers/sites_controller.rb
-- json info now includes photos

modified:   app/models/audio_event.rb
modified:   app/models/site.rb
modified:   app/models/tag.rb
-- got rid of inverse_of settings

modified:   lib/modules/audio.rb
modified:   lib/modules/audio_ffmpeg.rb
modified:   lib/modules/audio_sox.rb
modified:   lib/modules/file_cacher.rb
-- mp3 files are no created and cached

modified:   lib/modules/spectrogram.rb
-- spectrogram gen. can now take account of window and sample rate

deleted:    media/cachedaudio/.gitkeep
deleted:    media/cachedimages/.gitkeep
-- these should not have been checked in

modified:   test/unit/module_audio_test.rb
modified:   test/unit/module_spectrogram_test.rb
-- added more tests, and they pass (I know, I was surprised too)
  • Loading branch information
cofiem committed Nov 23, 2012
1 parent d5b3557 commit 18305a5
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 30 deletions.
4 changes: 2 additions & 2 deletions app/controllers/sites_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ def index
# GET /sites/1
# GET /sites/1.json
def show
@site = Site.find(params[:id])
@site = Site.includes(:photos).find(params[:id])

respond_to do |format|
format.html # show.html.erb
format.json { render json: @site }
format.json { render json: @site.as_json(:includes => :photos) }
end
end

Expand Down
2 changes: 1 addition & 1 deletion app/models/audio_event.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class AudioEvent < ActiveRecord::Base
# relations
belongs_to :audio_recording
has_many :audio_event_tags, :inverse_of => :audio_event
has_many :audio_event_tags
has_many :tags, :through => :audio_event_tags

accepts_nested_attributes_for :tags, :audio_event_tags
Expand Down
11 changes: 11 additions & 0 deletions app/models/site.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,15 @@ class Site < ActiveRecord::Base
#scope :sites_in_project, lambda { |project_ids| where(Project.specified_projects, { :ids => project_ids } ) }
scope :site_projects, lambda{ |project_ids| includes(:projects).where(:projects => {:id => project_ids} ) }

# json formatting
def as_json(options={})
super(
:include =>
[
:photos,
:projects => {:only => [:id, :urn]},
]
)
end

end
2 changes: 1 addition & 1 deletion app/models/tag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ class Tag < ActiveRecord::Base
extend Enumerize

# relations
has_many :audio_event_tags, :inverse_of => :tag
has_many :audio_event_tags
has_many :audio_events, :through => :audio_event_tags

accepts_nested_attributes_for :audio_events, :audio_event_tags
Expand Down
39 changes: 26 additions & 13 deletions lib/modules/audio.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
require 'open3'
require 'string'

module Audio
include AudioSox, AudioMp3splt, AudioWavpack, AudioFfmpeg

# @return Path to a file. The file does not exist.
def self.temp_file(extension)
path = Rails.root.join('tmp', SecureRandom.hex(7)+'.'+extension.trim('.','')).to_s
path
end

# Provides information about an audio file.
def self.info(source)

Expand Down Expand Up @@ -30,27 +37,36 @@ def self.modify(source, target, modify_parameters)
raise ArgumentError "Source and Target are the same file." unless source != target

if source.match(/\.wv$/) && target.match(/\.wav$/)
raise ArgumentError, "Source must be a wavpack file: #{File.basename(source)}." unless source.match(/\.wv$/)
raise ArgumentError, "Target must be a wav file: #{File.basename(target)}." unless target.match(/\.wav$/)
raise ArgumentError, "Target must be a wav file, given #{modify_parameters[:format]}." unless modify_parameters[:format] == 'wav'

modify_wv_wav(source, target, modify_parameters)
AudioWavpack::modify_wavpack(source, target, modify_parameters)

elsif source.match(/\.wv$/) && target.match(/\.mp3$/)

# use cache to place intermediate file in the right place?
modify_wv_wav(source, target, modify_parameters)
# put the wav file in a temp location
temp_wav_file = temp_file('wav')
AudioWavpack::modify_wavpack(source, temp_wav_file, modify_parameters)

# remove start and end offset from modify_parameters (otherwise it will be done again!)
modify_wav_mp3(target, target, modify_parameters)
wav_to_mp3_modify_params = modify_parameters.clone
wav_to_mp3_modify_params.delete :start_offset
wav_to_mp3_modify_params.delete :end_offset

AudioSox::modify_sox(temp_wav_file, target, wav_to_mp3_modify_params)

elsif source.match(/\.wav$/)&& target.match(/\.mp3$/)

modify_wav_mp3(target, target, modify_parameters)
AudioSox::modify_sox(source, target, modify_parameters)

elsif source..match(/\.mp3$/) && target.match(/\.mp3$/)
elsif source.match(/\.mp3$/) && target.match(/\.mp3$/)

modify_mp3_mp3(source, target, modify_parameters)
AudioSox::modify_sox(source, target, modify_parameters)

else

AudioFfmpeg::modify_ffmpeg(target, target, modify_parameters)
AudioSox::modify_sox(target, target, modify_parameters)

end

Expand All @@ -69,13 +85,10 @@ def self.modify_wv_wav(source, target, modify_parameters)
raise ArgumentError, "Target must be a wav file: #{File.basename(target)}" unless target.match(/\.wav$/)

# wav pack can only be converted to wav
cached_wav_audio_parameters = modify_parameters.clone
cached_wav_audio_parameters[:format] = 'wav'
#cached_wav_audio_parameters = modify_parameters.clone
#cached_wav_audio_parameters[:format] = 'wav'

target_file = Cache::cached_audio_file cached_wav_audio_parameters
target_possible_paths = Cache::possible_paths(Cache::cached_audio_storage_paths,target_file)

AudioWavpack::modify_wavpack(source, target_possible_paths.first, modify_parameters)
end

# can do any required modifications (using sox or ffmpeg)
Expand Down
5 changes: 4 additions & 1 deletion lib/modules/audio_ffmpeg.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ module AudioFfmpeg
:format_long_name => 'WAV / WAVE (Waveform Audio)'
},
:mp3 => {

:codec_name => 'mp3',
:codec_long_name => 'MP3 (MPEG audio layer 3)',
:codec_type => 'audio',
:format_long_name => 'MP2/3 (MPEG audio layer 2/3)'
}
}

Expand Down
10 changes: 6 additions & 4 deletions lib/modules/audio_sox.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def self.modify_sox(source, target, modify_parameters = {})
raise ArgumentError, "Source is not a mp3 or wav file: #{File.basename(source)}" unless source.match(/\.mp3|\.wav$/)
raise ArgumentError, "Target is not a mp3 or wav file: : #{File.basename(target)}" unless target.match(/\.mp3|\.wav$/)
raise ArgumentError, "Source does not exist: #{File.basename(source)}" unless File.exists? source
raise ArgumentError, "Target exists: #{File.basename(target)}" unless !File.exists? source
raise ArgumentError, "Target exists: #{File.basename(target)}" unless !File.exists? target

result = {}

Expand All @@ -46,13 +46,13 @@ def self.modify_sox(source, target, modify_parameters = {})
# start and end offset
if modify_parameters.include? :start_offset
start_offset_formatted = Time.at(modify_parameters[:start_offset]).utc.strftime('%H:%M:%S.%2N')
arguments += "trim #{start_offset_formatted}"
arguments += "trim =#{start_offset_formatted}"
end

if modify_parameters.include? :end_offset
end_offset_formatted = Time.at(modify_parameters[:start_offset]).utc.strftime('%H:%M:%S.%2N')
end_offset_formatted = Time.at(modify_parameters[:end_offset]).utc.strftime('%H:%M:%S.%2N')
if modify_parameters.include? :start_offset
arguments += " #{end_offset_formatted}"
arguments += " =#{end_offset_formatted}"
else
# if start offset was not included, include audio from the start of the file.
arguments += "trim 0 #{end_offset_formatted}"
Expand All @@ -78,6 +78,8 @@ def self.modify_sox(source, target, modify_parameters = {})
sox_command = "#@sox_path -V4 \"#{source}\" \"#{target}\" #{arguments}" # commands to get info from audio file
sox_stdout_str, sox_stderr_str, sox_status = Open3.capture3(sox_command) # run the commands and wait for the result

puts "Sox command #{sox_command}"

if sox_status.exitstatus != 0 || !File.exists?(target)
raise "Sox exited with an error: #{sox_stderr_str}"
end
Expand Down
6 changes: 5 additions & 1 deletion lib/modules/file_cacher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ def self.generate_spectrogram(modify_parameters = {})
source_existing_paths = Cache::existing_paths(Cache::cached_audio_storage_paths,source_file)

if source_existing_paths.blank?
# change the format to wav, so spectrograms can be created from the audio
audio_modify_parameters = modify_parameters.clone
audio_modify_parameters[:format] = 'wav'

# if no cached audio files exist, try to create them
create_audio_segment modify_parameters
create_audio_segment audio_modify_parameters
source_existing_paths = Cache::existing_paths(Cache::cached_audio_storage_paths,source_file)
# raise an exception if the cached audio files could not be created
raise Exceptions::AudioFileNotFoundError, "Could not generate spectrogram." if source_existing_paths.blank?
Expand Down
29 changes: 22 additions & 7 deletions lib/modules/spectrogram.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
require 'open3'
require 'OS'

module Spectrogram
include OS

@sox_path = if OS.windows? then "./vendor/bin/sox/windows/sox.exe" else "sox" end
@sox_arguments_immutable = '-n '
@sox_arguments_verbose = "-V"
@sox_arguments_output_audio = "-n"
@sox_arguments_sample_rate = "rate 22050"
@sox_arguments_spectrogram = "spectrogram -m -r -l -a -q 249 -w hann -y 257 -X 43.06640625 -z 100"
@sox_arguments_output = "-o"

Expand All @@ -27,13 +24,31 @@ def self.window_options()
# possible parameters: :window :colour :format
def self.generate(source, target, modify_parameters)
raise ArgumentError, "Target path for spectrogram generation already exists: #{target}." unless !File.exist?(target)
raise ArgumentError, "Window size must be one of ''#{window_options.join(', ')}', given '#{modify_parameters[:window]}'." unless window_options.include? modify_parameters[:window].to_i

# sample rate
sample_rate_param = modify_parameters.include?(:sample_rate) ? modify_parameters[:sample_rate].to_i : 11025

# window size
all_window_options = window_options.join(', ')
window_param = modify_parameters.include?(:window) ? modify_parameters[:window].to_i : 512
raise ArgumentError, "Window size must be one of '#{all_window_options}', given '#{window_param}'." unless window_options.include? window_param

# window size must be one more than a power of two, see sox documentation http://sox.sourceforge.net/sox.html
window_param = (window_param / 2) + 1
window_settings = ' -y '+window_param.to_s

# colours
colours_available = colour_options.map { |k, v| "#{k} (#{v})" }.join(', ')
raise ArgumentError, "Colour must be one of '#{colours_available}', given '#{modify_parameters[:colour]}'." unless colour_options.include? modify_parameters[:colour].to_sym
colour_param = modify_parameters.include?(:colour) ? modify_parameters[:colour] : 'g'
raise ArgumentError, "Colour must be one of '#{colours_available}', given '#{}'." unless colour_options.include? colour_param.to_sym
colour_settings = ' -m -q 249 -z 100'


# sox command to create a spectrogram from an audio file
command = "#@sox_path -V \"#{source}\" -n #@sox_arguments_sample_rate #@sox_arguments_spectrogram #@sox_arguments_output \"#{target}\""
# -V is for verbose
# -n indicates no output audio file
spectrogram_settings = 'spectrogram -r -l -a -w Hamming -X 43.06640625' + colour_settings + window_settings
command = "#@sox_path -V \"#{source}\" -n rate #{sample_rate_param} #{spectrogram_settings} #@sox_arguments_output \"#{target}\""

# run the command and wait for the result
stdout_str, stderr_str, status = Open3.capture3(command)
Expand Down

0 comments on commit 18305a5

Please sign in to comment.