'Audio Frequency Separation In Python

I'm working on a nerd project for fun. The project is analog video recorded onto an audio cassette. The challenge lies in the very limited bandwidth. I have a method to record color video at 24fps along with mono audio. I got the video stuff working but need some help with the audio. Here is the signal I have to work with:

Note: using YUV color space

Left channel: Sync Pulses & Y (luma) data

Right channel: U & V (chroma) data mixed with Mono audio (Amplitude Modulated at 14kHz)

I'm not sure how to separate the color data from the audio. I've looked into FFT with numpy a bit but am not fully understanding it.

Basically what I need is a band filter to separate 13990Hz - 14010Hz (to account for wow and flutter)



Solution 1:[1]

Ok here is a little test code that shows how this works.

import matplotlib.pyplot as plt
import numpy as np
import math as mt
from scipy.signal import butter, sosfilt, lfilter

def butter_bandpass(lowcut, highcut, fs, order=5):
        nyq = 0.5 * fs
        low = lowcut / nyq
        high = highcut / nyq
        sos = butter(order, [low, high], analog=False, btype='band', output='sos')
        return sos

def bandpass(data, lowcut, highcut, fs, order=5):
        sos = butter_bandpass(lowcut, highcut, fs, order=order)
        y = sosfilt(sos, data)
        return y

def bandstop(data, lowcut, highcut, fs, order):
    nyq = 0.5 * fs
    low = lowcut / nyq
    high = highcut / nyq

    i, u = butter(order, [low, high], btype='bandstop')
    y = lfilter(i, u, data)
    return y

# Modulation & Bandpass Test

# note that the data is just test data and isn't actual audio or color data

Fs = 192000 # rate
f = 14000 # carrier frequency (Hz)
sample = 192000 # total length

x = np.arange(sample)
signal = (np.sin(2 * np.pi * 1000 * x / Fs)) # audio
noise = 0.5*(np.sin(2 * np.pi * 10000 * x / Fs)) # color data

y = [] # combined AM audio and color data

for t in range(0, sample, 1):
    amp = (signal[t] + 1) / 2
    sine = amp * (mt.sin(2*mt.pi * f * t / Fs))
    y.append((sine+noise[t])/2)

# y is raw signal
w = bandpass(y, 1600, 1800, 24000, order=1) # only AM audio signal
v = bandstop(y, 1450, 1950, 24000, order=6) # Rest of the signal
# Note: lowered the sample rate input for the filters (and frequencies accordingly) 
# since it didn't like 192khz

# The color data does get impacted by the bandstop filter but this is 
# mostly not noticable as the YUV color space is used meaning
# the color resolution can be reduced drastically without noticable effect
    
plt.plot(y)
plt.plot(w)
plt.plot(v)
plt.xlabel('sample')
plt.ylabel('amplitude')
plt.show()

If you want to check out the full code along with a wav of the signal and an example video of the output here's a link: https://drive.google.com/drive/folders/18ogpK4n43d_Q0tjdmlm2uIRZBIrRu01y?usp=sharing

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Jake A