Class: Wavesync::Audio
- Inherits:
-
Object
- Object
- Wavesync::Audio
- Defined in:
- lib/wavesync/audio.rb
Constant Summary collapse
- SUPPORTED_FORMATS =
%w[.m4a .mp3 .wav .aif .aiff].freeze
- ID3V2_FRAME_TITLE =
'TIT2'- ID3V2_FRAME_ARTIST =
'TPE1'- ID3V2_FRAME_ALBUM =
'TALB'- ID3V2_FRAME_ALBUM_ARTIST =
'TPE2'- ID3V2_FRAME_GENRE =
'TCON'- ID3V2_FRAME_COMPOSER =
'TCOM'- ID3V2_FRAME_ENCODED_BY =
'TENC'- ID3V2_FRAME_COMPILATION =
'TCMP'- FRAME_ID_TO_FFMPEG_KEY =
{ ID3V2_FRAME_TITLE => 'title', ID3V2_FRAME_ARTIST => 'artist', ID3V2_FRAME_ALBUM => 'album', ID3V2_FRAME_ALBUM_ARTIST => 'album_artist', ID3V2_FRAME_GENRE => 'genre', ID3V2_FRAME_COMPOSER => 'composer', ID3V2_FRAME_ENCODED_BY => 'encoded_by', ID3V2_FRAME_COMPILATION => 'compilation' }.freeze
- FFMPEG_KEY_TO_FRAME_ID =
FRAME_ID_TO_FFMPEG_KEY.invert.freeze
Class Method Summary collapse
-
.find_all(library_path) ⇒ Object
: (String library_path) -> Array.
Instance Method Summary collapse
-
#bit_depth ⇒ Object
: () -> Integer?.
-
#bitrate ⇒ Object
: () -> Integer?.
-
#bpm ⇒ Object
: () -> (String | Integer)?.
-
#cue_points ⇒ Object
: () -> Array.
-
#duration ⇒ Object
: () -> Float.
-
#format ⇒ Object
: () -> AudioFormat.
-
#initialize(file_path) ⇒ Audio
constructor
: (String file_path) -> void.
-
#loops ⇒ Object
: () -> Array.
-
#sample_rate ⇒ Object
: () -> Integer?.
-
#transcode(target_path, target_sample_rate: nil, target_file_type: nil, target_bit_depth: nil, padding_seconds: nil, metadata: {}, target_bitrate: 192) ⇒ Object
: (String target_path, ?target_sample_rate: Integer?, ?target_file_type: String?, ?target_bit_depth: Integer?, ?padding_seconds: Numeric?, ?metadata: Hash[String, String], ?target_bitrate: Integer) ?{ (String) -> void } -> bool.
-
#transliterate_tags ⇒ Object
: () -> void.
-
#transliterated_tag_changes ⇒ Object
: () -> Hash[String, String].
-
#write_bpm(bpm) ⇒ Object
: (String | Integer | Float bpm) -> void.
-
#write_cue_points(cue_points) ⇒ Object
: (Array cue_points) -> void.
Constructor Details
#initialize(file_path) ⇒ Audio
: (String file_path) -> void
24 25 26 27 28 |
# File 'lib/wavesync/audio.rb', line 24 def initialize(file_path) @file_path = file_path #: String @file_ext = File.extname(@file_path).downcase #: String @audio = Wavesync::FFMPEG::Probe.new(file_path) #: Wavesync::FFMPEG::Probe end |
Class Method Details
.find_all(library_path) ⇒ Object
: (String library_path) -> Array
17 18 19 20 21 |
# File 'lib/wavesync/audio.rb', line 17 def self.find_all(library_path) Dir.glob(File.join(library_path, '**', '*')) .select { |file| SUPPORTED_FORMATS.include?(File.extname(file).downcase) } .sort_by(&:downcase) end |
Instance Method Details
#bit_depth ⇒ Object
: () -> Integer?
41 42 43 |
# File 'lib/wavesync/audio.rb', line 41 def bit_depth @bit_depth ||= @audio.bit_depth end |
#bitrate ⇒ Object
: () -> Integer?
46 47 48 |
# File 'lib/wavesync/audio.rb', line 46 def bitrate @bitrate ||= @audio.bitrate end |
#bpm ⇒ Object
: () -> (String | Integer)?
51 52 53 54 55 |
# File 'lib/wavesync/audio.rb', line 51 def bpm return @bpm if defined?(@bpm) @bpm = bpm_from_file end |
#cue_points ⇒ Object
: () -> Array
68 69 70 71 72 |
# File 'lib/wavesync/audio.rb', line 68 def cue_points return [] unless @file_ext == '.wav' CueChunk.read(@file_path) end |
#duration ⇒ Object
: () -> Float
31 32 33 |
# File 'lib/wavesync/audio.rb', line 31 def duration @audio.duration end |
#format ⇒ Object
: () -> AudioFormat
58 59 60 61 62 63 64 65 |
# File 'lib/wavesync/audio.rb', line 58 def format AudioFormat.new( file_type: @file_ext.delete_prefix('.'), sample_rate: sample_rate, bit_depth: bit_depth, bitrate: bitrate ) end |
#loops ⇒ Object
: () -> Array
75 76 77 |
# File 'lib/wavesync/audio.rb', line 75 def loops CueChunk.loops(cue_points) end |
#sample_rate ⇒ Object
: () -> Integer?
36 37 38 |
# File 'lib/wavesync/audio.rb', line 36 def sample_rate @sample_rate ||= @audio.sample_rate end |
#transcode(target_path, target_sample_rate: nil, target_file_type: nil, target_bit_depth: nil, padding_seconds: nil, metadata: {}, target_bitrate: 192) ⇒ Object
: (String target_path, ?target_sample_rate: Integer?, ?target_file_type: String?, ?target_bit_depth: Integer?, ?padding_seconds: Numeric?, ?metadata: Hash[String, String], ?target_bitrate: Integer) ?{ (String) -> void } -> bool
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/wavesync/audio.rb', line 150 def transcode(target_path, target_sample_rate: nil, target_file_type: nil, target_bit_depth: nil, padding_seconds: nil, metadata: {}, target_bitrate: 192) ext = target_file_type || @file_ext.delete_prefix('.') temp_path = File.join(Dir.tmpdir, "wavesync_transcode_#{SecureRandom.hex}.#{ext}") begin command = Wavesync::FFMPEG.new.input(@file_path).audio_codec(transcode_codec(ext, target_bit_depth)) command.audio_bitrate("#{target_bitrate}k") if ext == 'mp3' command.sample_rate(target_sample_rate) if target_sample_rate if padding_seconds&.positive? total_duration = @audio.duration + padding_seconds command.audio_filter("apad=whole_dur=#{total_duration.round(6)}") end .each { |key, value| command.(key, value) } Timing.current.measure(:transcode) { command.run(temp_path) } yield temp_path if block_given? Timing.current.measure(:copy) { FileUtils.install(temp_path, target_path) } true rescue SystemCallError => e Logger.log_error(e, call_site: 'Audio#transcode', arguments: { target_path:, target_sample_rate:, target_file_type:, target_bit_depth:, padding_seconds:, target_bitrate: }) false ensure FileUtils.rm_f(temp_path) end end |
#transliterate_tags ⇒ Object
: () -> void
125 126 127 128 129 130 131 132 |
# File 'lib/wavesync/audio.rb', line 125 def return unless @file_ext == '.mp3' changes = transliterated_tag_changes return if changes.empty? (changes) end |
#transliterated_tag_changes ⇒ Object
: () -> Hash[String, String]
109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/wavesync/audio.rb', line 109 def transliterated_tag_changes = @audio. changes = {} #: Hash[String, String] FRAME_ID_TO_FFMPEG_KEY.each_value do |ffmpeg_key| current_value = (, ffmpeg_key) next if current_value.nil? transliterated = transliterate(current_value) changes[ffmpeg_key] = transliterated if transliterated != current_value end changes end |
#write_bpm(bpm) ⇒ Object
: (String | Integer | Float bpm) -> void
135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/wavesync/audio.rb', line 135 def write_bpm(bpm) case @file_ext when '.m4a' write_bpm_to_m4a(bpm) when '.mp3' write_bpm_to_mp3(bpm) when '.wav' write_bpm_to_wav(bpm) when '.aif', '.aiff' write_bpm_to_aiff(bpm) end @bpm = bpm end |