| 1 | # The SplitFire naming, i.e; camel-cased style, is only being used for branding. |
| 2 | # Inside the code, we refer this tool as Splitfire. |
| 3 | |
| 4 | # This meant tobe just a demo job. |
| 5 | class SplitfireDemoJob < ApplicationJob |
| 6 | # TODO: Set priority based on account type. |
| 7 | queue_as :default |
| 8 | |
| 9 | def perform(audio_file_id) |
| 10 | # Get the file to split. |
| 11 | @file = AudioFile.find_by(id: audio_file_id) |
| 12 | |
| 13 | # Make sure the actual file is attached. |
| 14 | # Otherwise, we don't want to do the job. |
| 15 | return if @file.nil? || !@file.source_file.attached? |
| 16 | |
| 17 | file_path = ActiveStorage::Blob.service.send(:path_for, @file.source_file.key) |
| 18 | |
| 19 | broadcast_progress(20) |
| 20 | |
| 21 | # Change to spleeter working directory. |
| 22 | if Rails.env.production? |
| 23 | Dir.chdir('/home/deploy/apps/spleeter/') |
| 24 | else |
| 25 | Dir.chdir('../spleeter/') |
| 26 | end |
| 27 | |
| 28 | # Compute the params. |
| 29 | splitfire_setting = UserSetting.find_by(user_id: 69) # Always use @splitfire setting. |
| 30 | spleeter_param = splitfire_setting.model.to_s |
| 31 | spleeter_param = "#{spleeter_param}-#{splitfire_setting.frequency}" if splitfire_setting.frequency == '16kHz' |
| 32 | @codec = 'mp3' |
| 33 | |
| 34 | broadcast_progress(30) |
| 35 | |
| 36 | # Spleet it! Always use the fullpath to the Spleeter. |
| 37 | if Rails.env.production? |
| 38 | system("/home/deploy/.local/bin/spleeter separate -p spleeter:#{spleeter_param} -o ./output/ #{file_path} -c #{@codec} >> spleeter_log.txt") |
| 39 | else |
| 40 | system("spleeter separate -p spleeter:#{spleeter_param} -o ./output/ #{file_path} -c #{@codec} >> spleeter_log.txt") |
| 41 | end |
| 42 | |
| 43 | broadcast_progress(50) |
| 44 | |
| 45 | @file_output_directory = "output/#{@file.source_file.blob.key}" |
| 46 | |
| 47 | # Start from the beginning. |
| 48 | SplitfireResult |
| 49 | .where(audio_file_id: @file.id) |
| 50 | .destroy_all |
| 51 | |
| 52 | compose_result_vocals |
| 53 | compose_result_bass |
| 54 | compose_result_drums |
| 55 | compose_result_other |
| 56 | compose_result_piano |
| 57 | |
| 58 | broadcast_progress(90) |
| 59 | |
| 60 | # Cleanup Spleeter output folder |
| 61 | system("rm -rf #{@file_output_directory}") |
| 62 | |
| 63 | @file.audio_file_splitfire_status.done! |
| 64 | |
| 65 | broadcast_progress(100) |
| 66 | end |
| 67 | |
| 68 | private |
| 69 | |
| 70 | def compose_result_vocals |
| 71 | result_vocals_path = "#{@file_output_directory}/vocals.#{@codec}" |
| 72 | |
| 73 | waiting_console(result_vocals_path) |
| 74 | |
| 75 | result_vocals_file = File.open(result_vocals_path) |
| 76 | result_vocals_filename = "vocals-#{@file.id}.#{@codec}" |
| 77 | result_vocals = SplitfireResult.new(audio_file_id: @file.id) |
| 78 | |
| 79 | result_vocals.source_file.attach(io: result_vocals_file, filename: result_vocals_filename) |
| 80 | result_vocals.save |
| 81 | end |
| 82 | |
| 83 | def compose_result_drums |
| 84 | result_drums_path = "#{@file_output_directory}/drums.#{@codec}" |
| 85 | |
| 86 | waiting_console(result_drums_path) |
| 87 | |
| 88 | result_drums_file = File.open(result_drums_path) |
| 89 | result_drums_filename = "drums-#{@file.id}.#{@codec}" |
| 90 | result_drums = SplitfireResult.new(audio_file_id: @file.id) |
| 91 | |
| 92 | result_drums.source_file.attach(io: result_drums_file, filename: result_drums_filename) |
| 93 | result_drums.save |
| 94 | end |
| 95 | |
| 96 | def compose_result_bass |
| 97 | result_bass_path = "#{@file_output_directory}/bass.#{@codec}" |
| 98 | |
| 99 | waiting_console(result_bass_path) |
| 100 | |
| 101 | result_bass_file = File.open(result_bass_path) |
| 102 | result_bass_filename = "bass-#{@file.id}.#{@codec}" |
| 103 | result_bass = SplitfireResult.new(audio_file_id: @file.id) |
| 104 | |
| 105 | result_bass.source_file.attach(io: result_bass_file, filename: result_bass_filename) |
| 106 | result_bass.save |
| 107 | end |
| 108 | |
| 109 | def compose_result_other |
| 110 | result_other_path = "#{@file_output_directory}/other.#{@codec}" |
| 111 | |
| 112 | waiting_console(result_other_path) |
| 113 | |
| 114 | result_other_file = File.open(result_other_path) |
| 115 | result_other_filename = "other-#{@file.id}.#{@codec}" |
| 116 | result_other = SplitfireResult.new(audio_file_id: @file.id) |
| 117 | |
| 118 | result_other.source_file.attach(io: result_other_file, filename: result_other_filename) |
| 119 | result_other.save |
| 120 | end |
| 121 | |
| 122 | def compose_result_piano |
| 123 | result_piano_path = "#{@file_output_directory}/piano.#{@codec}" |
| 124 | |
| 125 | waiting_console(result_piano_path) |
| 126 | |
| 127 | result_piano_file = File.open(result_piano_path) |
| 128 | result_piano_filename = "piano-#{@file.id}.#{@codec}" |
| 129 | result_piano = SplitfireResult.new(audio_file_id: @file.id) |
| 130 | |
| 131 | result_piano.source_file.attach(io: result_piano_file, filename: result_piano_filename) |
| 132 | result_piano.save |
| 133 | end |
| 134 | |
| 135 | def waiting_console(file_path) |
| 136 | until File.exist?(file_path) |
| 137 | system("echo 'We are waiting for #{file_path}...'") |
| 138 | sleep 60 |
| 139 | end |
| 140 | end |
| 141 | |
| 142 | def broadcast_progress(progress) |
| 143 | @file.audio_file_splitfire_status.update_attribute(:processing_progress, progress) |
| 144 | ActionCable.server.broadcast('FileProcessingChannel', @file.as_json_packed) |
| 145 | end |
| 146 | end |