Skip to content

Commit

Permalink
Refactor and add comments.
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxPower15 committed Feb 10, 2024
1 parent b844156 commit d3e3e92
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 51 deletions.
61 changes: 61 additions & 0 deletions calculations.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
def frame_duration
1024.0 / 44100.0 * 1000000.0
end

def get_closest_aligned_time(target_time)
decimal_frames_to_target_time = target_time.to_f / frame_duration
nearest_frame_index_for_target_time = decimal_frames_to_target_time.round
puts "target_time: #{target_time}, decimal_frames_to_target_time: #{decimal_frames_to_target_time}, nearest_frame_index_for_target_time: #{nearest_frame_index_for_target_time}"
nearest_frame_index_for_target_time * frame_duration
end

def generate_command_and_directives_for_segment(index, target_start, target_end)
puts "--- segment #{index + 1} ---"

start_time = get_closest_aligned_time(target_start)
end_time = get_closest_aligned_time(target_end)
puts "start_time: #{start_time}, end_time: #{end_time}"

real_duration = end_time - start_time
puts "real_duration: #{real_duration}"

start_time_with_padding = [start_time - frame_duration * 2, 0].max
end_time_with_padding = end_time + frame_duration * 2
puts "start_time_with_padding: #{start_time_with_padding}, end_time_with_padding: #{end_time_with_padding}"

inpoint = 0

if index > 0
# We ask to also encode two frames before the start of our segment because
# the AAC format is interframe. That is, the encoding of each frame depends
# on the previous frame. This is also why AAC pads the start with silence.
# By adding some extra padding ourselves, we ensure that the "real" data we
# want will have been encoded as if the correct data preceded it. (Because
# it did!)
start_time_with_padding = [start_time_with_padding - frame_duration * 2, 0].max

# Although we only asked for two frames of padding, ffmpeg will add an
# additional 2 frames of silence at the start of the segment. When we slice out
# our real data with inpoint and outpoint, we'll want remove both the silence
# and the extra frames we asked for.
inpoint = frame_duration * 4
end

padded_duration = end_time_with_padding - start_time_with_padding
puts "padded_duration: #{padded_duration}"

# inpoint is inclusive and outpoint is exclusive. To avoid overlap, we subtract
# the duration of one frame from the outpoint.
outpoint = inpoint + real_duration - frame_duration

puts "inpoint: #{inpoint}, outpoint: #{outpoint}"

command = "ffmpeg -hide_banner -loglevel error -nostats -y -ss #{start_time_with_padding}us -t #{padded_duration}us -i out/#{SINE_WAVE_FILE_NAME} -c:a libfdk_aac -ar 44100 -f adts out/seg#{index + 1}.aac"
directives = [
"file 'seg#{index + 1}.aac'",
"inpoint #{inpoint}us",
"outpoint #{outpoint}us"
]

[command, directives.join("\n")]
end
60 changes: 9 additions & 51 deletions run.rb
Original file line number Diff line number Diff line change
@@ -1,82 +1,40 @@
require 'fileutils'
require "fileutils"
require_relative "./calculations"

# Feel free to change these constants for your own testing.
SINE_WAVE_DURATION = 10.to_f
SINE_FREQUENCY = 1000.to_f
TARGET_SEGMENT_DURATION = 1.0.to_f
SINE_WAVE_FILE_NAME = "sine-wave-#{SINE_WAVE_DURATION.to_i}-seconds.wav"

def frame_duration
1024.0 / 44100.0 * 1000000.0
end

def get_closest_aligned_time(target_time)
decimal_frames_to_target_time = target_time.to_f / frame_duration
nearest_frame_index_for_target_time = decimal_frames_to_target_time.round
puts "target_time: #{target_time}, decimal_frames_to_target_time: #{decimal_frames_to_target_time}, nearest_frame_index_for_target_time: #{nearest_frame_index_for_target_time}"
nearest_frame_index_for_target_time * frame_duration
end

def generate_command_and_directives_for_segment(index, target_start, target_end)
puts "--- segment #{index + 1} ---"

start_time = get_closest_aligned_time(target_start)
end_time = get_closest_aligned_time(target_end)
puts "start_time: #{start_time}, end_time: #{end_time}"

real_duration = end_time - start_time
puts "real_duration: #{real_duration}"

start_time_with_padding = [start_time - frame_duration * 2, 0].max
end_time_with_padding = end_time + frame_duration * 2
puts "start_time_with_padding: #{start_time_with_padding}, end_time_with_padding: #{end_time_with_padding}"

inpoint = 0
if index > 0
inpoint = frame_duration * 2
start_time_with_padding = [start_time_with_padding - frame_duration * 2, 0].max
inpoint += frame_duration * 2
end

padded_duration = end_time_with_padding - start_time_with_padding
puts "padded_duration: #{padded_duration}"

outpoint = inpoint + real_duration - frame_duration

puts "inpoint: #{inpoint}, outpoint: #{outpoint}"

command = "ffmpeg -hide_banner -loglevel error -nostats -y -ss #{start_time_with_padding}us -t #{padded_duration}us -i out/#{SINE_WAVE_FILE_NAME} -c:a libfdk_aac -ar 44100 -f adts out/seg#{index + 1}.aac"
directives = [
"file 'seg#{index + 1}.aac'",
"inpoint #{inpoint}us",
"outpoint #{outpoint}us"
]

[command, directives.join("\n")]
end

FileUtils.rm_rf "out"
FileUtils.mkdir_p "out"

# generate the sine wave we'll use as input
system("ffmpeg -hide_banner -loglevel error -nostats -y -f lavfi -i \"sine=frequency=#{SINE_FREQUENCY}:duration=#{SINE_WAVE_DURATION}\" out/#{SINE_WAVE_FILE_NAME}")

# Generate the commands we'll use to slice the sine wave into segments and the
# directives we'll use to recombine them later.
commands_and_directives = (SINE_WAVE_DURATION / TARGET_SEGMENT_DURATION).ceil.to_i.times.map do |i|
start_time = (i * TARGET_SEGMENT_DURATION * 1000000).round.to_i
end_time = [((i + 1) * TARGET_SEGMENT_DURATION * 1000000).round, SINE_WAVE_DURATION * 1000000].min.to_i
cmd, directives = generate_command_and_directives_for_segment(i, start_time, end_time)
generate_command_and_directives_for_segment(i, start_time, end_time)
end

all_directives = commands_and_directives.map { |cmd, directives| directives }.join("\n")
File.write("out/audio-concat.txt", all_directives)

puts "---"

# Run the commands.
commands_and_directives.each do |cmd, _|
puts cmd
system(cmd)
end

puts "---"

# Stitch the segments back together.
concat_cmd = "ffmpeg -hide_banner -loglevel error -nostats -y -f concat -i out/audio-concat.txt -c copy out/stitched.mp4"
puts concat_cmd
puts all_directives
Expand Down

0 comments on commit d3e3e92

Please sign in to comment.