Dup Ver Goto 📝

Batch Highpass Filter in FFmpeg

PT2/media/ffmpeg ffmpeg audio filter does not exist
To
76 lines, 327 words, 2157 chars Page 'BatchHpFilter' does not exist.

I wrote this for a 16 core 5950X, as ffmpeg's audio processing is single threaded. I haven't benchmarked if you get more performance using 32 queues (one per hardware thread rather than one per hardware core).

The main purpose is to have high-passed versions of a number of tracks or mixes, so that I can play them later through my hifi with the bass rolled off for the reason of being friendly to neighbours.

#!/usr/bin/env python

num_cores = 16

from glob import glob
from subprocess import run, PIPE, DEVNULL
from threading import Thread
import os, sys, re
from icecream import ic; ic.configureOutput(includeContext=True)

infiles = glob("*/*.m*")    # all files are .m4a and .mp3 so this glob will do
outfiles = [ "../hp_mp3/"+x for x in infiles ]
zfiles = list(zip(infiles,outfiles))

# we have a list of lists, one per core
zfs = [[] for i in range(num_cores)]

# We assign the files to queues in a round-robin fashion
for i,x in enumerate(zfiles):
  zfs[i%num_cores].append(x)

# do one file
def work(xs):
  ifn,ofn = xs

  # get bitrate of input file, so we can use the same bitrate on the encoded file
  m = run(["ffprobe","-hide_banner",ifn],stdin=DEVNULL,stdout=PIPE,stderr=PIPE)
  e = m.stderr.decode()
  es = e.splitlines()
  fs = [ x for x in es if "Audio:" in x ] # we'll get one line per audio stream
  if len(fs) == 0:
    ic("# fail",xs) # shouldn't happen
    return

  m = re.search(r"(\d+) kb/s",fs[0]) # hopefully this line includes a xxx kb/s bitrate
  if not m:
    ic("# fail2",xs) # shouldn't happen
    return
  br = m.group(1)
  br = f"{br}k"

  # do the transcoding
  run(["ffmpeg","-i",ifn,"-b:a",br,"-af","highpass=f=200,highpass=f=200",ofn],stdin=DEVNULL)

# takes a list of (infile/outfile) pairs and processes them one after another
class Worker:
  def __init__(self,zfs):
    self.zfs = zfs
  def __call__(self):
    for xs in self.zfs:
      work(xs)

threads = []

# create one thread per queue
for i,z in zfs.items():
  thread = Thread(target=Worker(z))
  threads.append(thread)

# start all the threads
for thread in threads:
  thread.start()

# join all the threads
for thread in threads:
  thread.join()