Last active
January 28, 2020 14:33
-
-
Save sunny/0364252c256a4043bfdc97b68d78d235 to your computer and use it in GitHub Desktop.
Cut wavs using ffmpeg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
def ffprobe_duration(wav) | |
call( | |
"ffprobe " \ | |
"'#{wav}' " \ | |
"-show_entries format=duration " \ | |
"-of compact=p=0:nk=1 " \ | |
"-v 0" | |
) | |
end | |
def ffmpeg_silences(wav) | |
call( | |
"ffmpeg " \ | |
"-i '#{wav}' " \ | |
"-filter_complex '[0:a]silencedetect=n=-70dB:d=1.5[outa]' " \ | |
"-map '[outa]' " \ | |
"-f s16le " \ | |
"-y /dev/null " \ | |
"2>&1" | |
) | |
end | |
def ffmpeg_cut(wav, new_wav, from, to) | |
call( | |
"ffmpeg " \ | |
"-i '#{wav}' " \ | |
"-ss #{from} " \ | |
"-t #{to} " \ | |
"-aq 70 " \ | |
"-y '#{new_wav}' " \ | |
"-v 0 " \ | |
"2>&1" | |
) | |
end | |
def call(command) | |
# puts "------> #{command}" | |
`#{command}` | |
end | |
# Récupère les arguments passés. | |
wav = ARGV[0] | |
wav_dir = ARGV[1] || abort("Usage: #{$0} file dir [start_at] [end_cut]") | |
start_at = ARGV[2].to_f | |
end_cut = ARGV[3].to_f | |
# Appelle `ffprobe` pour avoir le temps total, transformé en chiffre décimal. | |
duration = ffprobe_duration(wav).to_f | |
# En soustrayant le temps total aux secondes de fin ça donne le temps à ne pas | |
# dépasser. | |
end_at = duration - end_cut | |
# Appelle ffmpeg en lui passant le nom du fichier, sépare en lignes (`.lines`), | |
# puis récupère une valeur pour chaque ligne (`.map`). | |
timestamps = ffmpeg_silences(wav).lines.map do |line| | |
# Affiche la ligne en cours, avec son retour à la ligne. | |
# print line | |
# Si la ligne correspond à "silence_start: …" ou "silence_end: …", | |
# retourne la valeur trouvée, transformée en chiffre décimal (`.to_f`). | |
$2.to_f if line =~ /silence_(start|end): (\S+)/ | |
# Sinon, retourne `nil`. | |
# Retire chaque valeur `nil` qui reste (`.compact`), | |
# ce qui correspond à des lignes qui ne contiennent pas de valeur de silence. | |
end.compact | |
# On ajoute au à la liste des temps le démarrage et la fin afin d'avoir les | |
# temps qui ne sont PAS des silences quand on les regroupera par paires. | |
timestamps = [0] + timestamps + [duration] | |
# Maintenant que `timestamps` contient une liste de chiffres, comme | |
# `[0, 2.23, 4.555, 12.33, …]`, on les groupe par deux (`each_slice`), | |
# ce qui donne un début et une fin comme `[[0, 2.23], [4.55, 12.33], …]`. | |
ranges = timestamps.each_slice(2) | |
# On retire (`.reject`) les temps qui ont un temps en dehors du début et de | |
# la fin souhaitée. On ne garde pas non plus les temps qui sont vides, au cas | |
# où (par exemple `[0, 0]`). | |
ranges = ranges.reject do |from, to| | |
to < start_at || end_at < from || to == from | |
end | |
# On transforme chaque paire pour s'assurer que chaque temps reste dans les | |
# bornes. Par exemple si on a gardé `[2.3, 5.0]` mais qu'on démarre à `2.9`, | |
# on va retourner `[2.9, 5.0]`. | |
ranges = ranges.map do |from, to| | |
[[from, start_at].max, [to, end_at].min] | |
end | |
# On boucle sur chaque paire de temps en récupérant son index. | |
ranges.each_with_index do |(from, to), index| | |
filename = "#{wav_dir}/#{index}.wav" | |
puts "#{filename}: #{from}-#{to}" | |
# Découpe un nouveau fichier en indicant son nom et en passant les timestamps. | |
ffmpeg_cut(wav, filename, from, to) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment