আমি নীলের উত্তরটি সবচেয়ে পছন্দ করেছি, কারণ এটি অডিও ফাইলগুলির মধ্যে পারস্পরিক সম্পর্ক প্রবর্তন করে না: কেবল একটি লাভের স্তর বেছে নিন এবং এটিতে সমস্ত কিছু সামঞ্জস্য করুন।
তবে আমার কাছে normalize-ogg
থাকা কিছু ফাইলের আউটপুট বিশ্লেষণ করতে আমার কিছু সমস্যা হয়েছিল । bc
এটির সাথে একটি বাজে সমস্যা রয়েছে : এটি আসল গোলটি করে না, এটি কেবল ছাঁটাই হয়।
সুতরাং শেষ পর্যন্ত আমি শেল স্ক্রিপ্টিং ছেড়ে দিলাম এবং পাইথনে চলে গেলাম।
দ্রষ্টব্য 1: এক্সিফ্টোল অংশটি ওভারকিল হতে পারে তবে আমি 100% নিশ্চিত হতে চেয়েছিলাম যে আসল বিটরেটটি সংরক্ষণ করা হবে।
Note2: এই হবে ওভাররাইট মুল, যদি আপনি তাদের সংরক্ষণ স্বাভাবিক-OGG গত কলে --backup ব্যবহার করতে চান। তবে আমি অনুলিপি পৃথক, নিরাপদ, ডিরেক্টরিতে রাখতে আরও ব্যবহারিক বলে মনে করেছি।
দ্রষ্টব্য: এই দ্রষ্টব্যটি ওজগ ফাইলগুলির সাথে ডিল করে, তবে এটি এমপি 3 এর সাথে খাপ খাওয়ানোর ক্ষেত্রে তুচ্ছ, কেবল "ওগ" এর উপস্থিতিগুলিকে "এমপি 3" এর সাথে প্রতিস্থাপন করে।
এই সমস্যাটি আমার গ্রহণ করা। সর্বশেষতম সংস্করণটি এখানে পাওয়া যাবে: regain.py
#!/usr/bin/python3
"""
Parallel normalize gains
"""
'
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'
# Absolute value, in dB for the desired gain of each file
TARGET_GAIN = -12
#
MAX_THREADS = 2
from subprocess import Popen, PIPE
from multiprocessing.dummy import Pool as ThreadPool
from os import listdir
import logging
def initlogger(logfile="log.log", mainlevel=logging.DEBUG,
filelevel=logging.DEBUG, consolelevel=logging.DEBUG):
'''initlogger'''
# create logger
logger = logging.getLogger()
logger.setLevel(mainlevel)
# create file handler which logs even debug messages
fh = logging.FileHandler(logfile)
fh.setLevel(filelevel)
# create console handler also logging at DEBUG level
ch = logging.StreamHandler()
ch.setLevel(consolelevel)
# create formatter and add it to the handlers
formatter = logging.Formatter("%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s] %(message)s")
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# add the handlers to the logger
logger.addHandler(fh)
logger.addHandler(ch)
def logcommand(command=[]):
'''logcommand'''
if not isinstance(command, list):
return "", "", -1
logging.info("Command:\n" + " ".join(command) + "\n")
proc = Popen(command, stdin=PIPE, stdout=PIPE, stderr=PIPE)
output, err = proc.communicate()
output = output.decode("utf-8")
err = err.decode("utf-8")
logging.info("Output:\n" + output + "\n")
logging.info("Error:\n" + err + "\n")
logging.info("Return Code:\n" + str(proc.returncode) + "\n")
return output, err, proc.returncode
def regain(target):
'''regain'''
logging.info("============================ Start File ============================")
logging.warning(target["name"])
logging.info("Extracting gain info.\n")
commandgetlevels = ['normalize-ogg', '-n', target["name"]]
output, err, retcode = logcommand(commandgetlevels)
level = output.split()[0]
logging.debug("Level: " + level)
if "dBFS" in level:
level = level.split("dBFS")[0]
level = level.replace(',', '.')
level = int(round(float(level)))
delta = target["gain"] - level
logging.info("Required adjustment: " + str(delta) + "\n")
if delta is 0:
logging.warning(target["name"] + " is already at the correct level")
return 0
logging.info("Extracting average bitrate.\n")
commandgetinfo = ['exiftool', target["name"]]
output, err, retcode = logcommand(commandgetinfo)
bitrate = '0'
for line in output.split('\n'):
if 'Nominal Bitrate' in line:
bitrate = line.split(':')[1].split()[0]
break
logging.info("Average bitrate is: " + str(bitrate) + "\n")
if bitrate is '0':
logging.error("No valid bitrate found, aborting conversion.\n")
exit(-1)
logging.info("Re-normalizing.\n")
commandrenormalize = ['normalize-ogg', '--ogg', '--bitrate', bitrate,
'-g', str(delta) + 'db', target["name"]]
output, err, retcode = logcommand(commandrenormalize)
if retcode is not 0:
log.error("Output:\n" + output)
log.error("err:\n" + err)
exit(retcode)
return retcode
# function to be mapped over
def parallelregain(gain=TARGET_GAIN, threads=MAX_THREADS):
'''parallelregain'''
logging.info("Creating thread pool with " + str(threads) + " elements.\n")
pool = ThreadPool(threads)
targets = []
files_list = listdir(".")
files_list.sort()
counter = 0
for filename in files_list:
if filename.endswith("ogg"):
target = {
"name":filename,
"gain":gain,
}
targets.append(target)
counter = counter + 1
pool.map(regain, targets)
pool.close()
pool.join()
if __name__ == "__main__":
initlogger(logfile="normalize.log", consolelevel=logging.WARNING)
parallelregain()