Skip to content

Instantly share code, notes, and snippets.

@CTXz
Created July 30, 2022 00:37
Show Gist options
  • Save CTXz/316f7888684f05d33aea1295d440fa80 to your computer and use it in GitHub Desktop.
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 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