Created
July 30, 2022 00:37
-
-
Save CTXz/316f7888684f05d33aea1295d440fa80 to your computer and use it in GitHub Desktop.
This script saved my 5 hour long recording of DJ sets at an event, bless it!
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
# This script saved my 5 hour long recording of DJ sets at an event, bless it! | |
# The recording was likely flawed due to a sampling rate missmatch between the interface and | |
# the recording software. The result was a recording that had a faulty sample or two | |
# every 48 samples. The audible effect of this is that the recording had a high pitched | |
# "bit-cursh-ish" sound. | |
# To fix the recordings, the script applies a linear interpolation every 48 samples | |
# (where the faulty samples are). While a linear interpolation isn't the most accurate | |
# interpolation method to restore audio, it actually suffices for errors of so few samples. | |
# The script uses pydub to read and manipulate the audio file. | |
# Note that the AudioSegment will load the whole audio file into memory, | |
# which will obviously cause memory issues for a typical DJ set recording. | |
# To fix this, split the recording into numerous smaller segments (via ex. sox) | |
# and then recombine them back together after the fix has been applied. | |
from ctypes import sizeof | |
from traceback import print_list | |
import numpy as np | |
from pydub import AudioSegment | |
from array import array | |
# Splits samples array into lists of left and right samples | |
# Parameters: | |
# samples: array of samples returned by pydub.AudioSegment.get_array_of_samples() | |
# Returns: | |
# left: list of left samples | |
# right: list of right samples | |
# | |
def split_sample_lr(samples): | |
left = samples[0::2] | |
right = samples[1::2] | |
return left, right | |
# Recombines left and right samples into a single array | |
# Parameters: | |
# left: list of left samples | |
# right: list of right samples | |
# Returns: | |
# samples: array of samples compatible with pydub.AudioSegment | |
# | |
def join_sample_lr(left, right): | |
samples = [] | |
for i in range(len(left)): | |
samples.append(left[i]) | |
samples.append(right[i]) | |
return array('i', samples) | |
# Linear interpolation between two samples | |
# Parameters: | |
# x: Sample to be fixed | |
# x0: Undamaged sample before x | |
# x1: Undamaged sample after x | |
# y0: Value of undamaged sample before x (samples[x0]) | |
# y1: Value of undamaged sample after x (samples[x1]) | |
# Returns: | |
# "Correct"/Fixed value of broken sample x | |
# | |
def lin_interpolate(x, x0, x1, y0, y1): | |
return y0 + (x - x0) * (y1 - y0) / (x1 - x0) | |
############################################################################################# | |
# MAIN | |
############################################################################################# | |
START = 27 # First sample to be fixed | |
for i in range (1, 96, 1): | |
fname = "Pt5" + str(i).zfill(3) # Adjust this to match the filenames of the split recording | |
print("Fixing: " + fname) | |
audio = AudioSegment.from_wav("Pt5/" + fname + ".wav") # Adjust this to match the path of the split recording | |
samples = audio.get_array_of_samples() | |
l, r = split_sample_lr(samples) | |
# Fix Left Channel | |
for i in range(START, len(l)-3, 48): | |
x0 = i-1 | |
x1 = i+2 | |
y0 = l[x0] | |
y1 = l[x1] | |
l[i] = round(lin_interpolate(i, x0, x1, y0, y1)) # Fix first broken sample | |
l[i+1] = round(lin_interpolate(i+1, x0, x1, y0, y1)) # Fix second broken sample | |
# Fix Right Channel | |
for i in range(START, len(r)-3, 48): | |
x0 = i-1 | |
x1 = i+2 | |
y0 = r[x0] | |
y1 = r[x1] | |
r[i] = round(lin_interpolate(i, x0, x1, y0, y1)) # Fix first broken sample | |
r[i+1] = round(lin_interpolate(i+1, x0, x1, y0, y1)) # Fix second broken sample | |
samples = join_sample_lr(l, r) | |
sample = audio._spawn(samples) | |
sample.export("Pt5/" + fname + "_Fixed.wav", format="wav") # Adjust this to match the path of the split recording |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment