Skip to content

Commit ce92544

Browse files
authored
Merge pull request #183 from eshaz/hifi-decode-spectral-nr
hifi-decode: Better resampling, Spectral Noise Reduction, Multi processing
2 parents 0bc2b9a + 61296da commit ce92544

File tree

7 files changed

+611
-348
lines changed

7 files changed

+611
-348
lines changed

filter_tune/filter_tune.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@
110110
from matplotlib.figure import Figure
111111

112112
from vhsdecode.utils import filtfft
113-
from vhsdecode.addons.FMdeemph import gen_high_shelf
114113
from vhsdecode.formats import get_format_params
115114
from vhsdecode import compute_video_filters
116115
from vhsdecode.main import supported_tape_formats

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ classifiers = [
1414
"Topic :: Multimedia :: Video",
1515
]
1616
dependencies = [
17-
"numpy>=1.22","numba>=0.58.1","scipy>=1.10.1","samplerate", "static-ffmpeg", "cython", "soundfile", "sounddevice", "importlib_resources >= 5.0; python_version < '3.10'", "matplotlib"
17+
"numpy>=1.22","numba>=0.58.1","scipy>=1.10.1","samplerate", "static-ffmpeg", "cython", "soundfile", "sounddevice", "importlib_resources >= 5.0; python_version < '3.10'", "matplotlib", "noisereduce>=2.0.0"
1818
]
1919
dynamic = ["version"]
2020
# version = "0.2.1dev0"

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ samplerate
66
scipy >=1.10,<=1.12
77
soundfile
88
sounddevice
9+
noisereduce

vhsdecode/addons/FMdeemph.py

Lines changed: 56 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,58 +10,63 @@
1010

1111

1212
@njit(cache=True)
13-
def gen_high_shelf(f0, dbgain, qfactor, fs):
14-
"""Generate high shelving filter coeficcients (digital).
15-
f0: The center frequency where the gain in decibel is at half the maximum value.
16-
Normalized to sampling frequency, i.e output will be filter from 0 to 2pi.
17-
dbgain: gain at the top of the shelf in decibels
18-
qfactor: determines shape of filter TODO: Document better
19-
fs: sampling frequency
20-
21-
TODO: Generate based on -3db
22-
Based on: https://www.w3.org/2011/audio/audio-eq-cookbook.html
23-
"""
24-
a = 10 ** (dbgain / 40.0)
25-
w0 = 2 * math.pi * (f0 / fs)
26-
alpha = math.sin(w0) / (2 * qfactor)
27-
28-
cosw0 = math.cos(w0)
29-
asquared = math.sqrt(a)
13+
def gen_shelf(f0, dbgain, type, fs, qfactor=None, bandwidth=None, slope=None):
14+
"""Generate shelving filter coefficients (digital).
15+
* f0:
16+
The center frequency where the gain in decibel is at half the maximum value.
17+
Normalized to sampling frequency, i.e output will be filter from 0 to 2pi.
18+
* dbgain:
19+
gain at the top of the shelf in decibels
20+
* fs:
21+
sampling frequency
22+
* type:
23+
"high" for high shelf, "low" for low shelf
24+
25+
and one of the following:
26+
* qfactor:
27+
determines shape of filter TODO: Document better
28+
* bandwidth:
29+
bandwidth in octaves between midpoint (dbGain / 2) gain frequencies
30+
* slope:
31+
shelf slope. When slope=1, the shelf slope is as steep as it can be and
32+
remain monotonically increasing or decreasing gain with frequency.
33+
The shelf slope, in dB/octave, remains proportional to S for all other
34+
values for a fixed and
3035
31-
b0 = a * ((a + 1) + (a - 1) * cosw0 + 2 * asquared * alpha)
32-
b1 = -2 * a * ((a - 1) + (a + 1) * cosw0)
33-
b2 = a * ((a + 1) + (a - 1) * cosw0 - 2 * asquared * alpha)
34-
a0 = (a + 1) - (a - 1) * cosw0 + 2 * asquared * alpha
35-
a1 = 2 * ((a - 1) - (a + 1) * cosw0)
36-
a2 = (a + 1) - (a - 1) * cosw0 - 2 * asquared * alpha
37-
return [b0, b1, b2], [a0, a1, a2]
38-
39-
40-
@njit(cache=True)
41-
def gen_low_shelf(f0, dbgain, qfactor, fs):
42-
"""Generate low shelving filter coeficcients (digital).
43-
f0: The center frequency where the gain in decibel is at half the maximum value.
44-
Normalized to sampling frequency, i.e output will be filter from 0 to 2pi.
45-
dbgain: gain at the top of the shelf in decibels
46-
qfactor: determines shape of filter TODO: Document better
47-
fs: sampling frequency
48-
49-
TODO: Generate based on -3db
5036
Based on: https://www.w3.org/2011/audio/audio-eq-cookbook.html
5137
"""
5238
a = 10 ** (dbgain / 40.0)
5339
w0 = 2 * math.pi * (f0 / fs)
54-
alpha = math.sin(w0) / (2 * qfactor)
40+
41+
if qfactor != None:
42+
alpha = math.sin(w0) / (2 * qfactor)
43+
elif bandwidth != None:
44+
alpha = math.sin(w0) * math.sinh((math.log(2) / 2) * bandwidth * (w0 / math.sin(w0)))
45+
elif slope != None:
46+
alpha = math.sin(w0 / 2) * math.sqrt((a + 1/ a ) * (1 / slope - 1) + 2)
47+
else:
48+
raise Exception("Must specify one value for either qfactor, bandwidth, or slope")
5549

5650
cosw0 = math.cos(w0)
5751
asquared = math.sqrt(a)
5852

59-
b0 = a * ((a + 1) - (a - 1) * cosw0 + 2 * asquared * alpha)
60-
b1 = 2 * a * ((a - 1) - (a + 1) * cosw0)
61-
b2 = a * ((a + 1) - (a - 1) * cosw0 - 2 * asquared * alpha)
62-
a0 = (a + 1) + (a - 1) * cosw0 + 2 * asquared * alpha
63-
a1 = -2 * ((a - 1) + (a + 1) * cosw0)
64-
a2 = (a + 1) + (a - 1) * cosw0 - 2 * asquared * alpha
53+
if type == "low":
54+
b0 = a * ((a + 1) - (a - 1) * cosw0 + 2 * asquared * alpha)
55+
b1 = 2 * a * ((a - 1) - (a + 1) * cosw0)
56+
b2 = a * ((a + 1) - (a - 1) * cosw0 - 2 * asquared * alpha)
57+
a0 = (a + 1) + (a - 1) * cosw0 + 2 * asquared * alpha
58+
a1 = -2 * ((a - 1) + (a + 1) * cosw0)
59+
a2 = (a + 1) + (a - 1) * cosw0 - 2 * asquared * alpha
60+
elif type == "high":
61+
b0 = a * ((a + 1) + (a - 1) * cosw0 + 2 * asquared * alpha)
62+
b1 = -2 * a * ((a - 1) + (a + 1) * cosw0)
63+
b2 = a * ((a + 1) + (a - 1) * cosw0 - 2 * asquared * alpha)
64+
a0 = (a + 1) - (a - 1) * cosw0 + 2 * asquared * alpha
65+
a1 = 2 * ((a - 1) - (a + 1) * cosw0)
66+
a2 = (a + 1) - (a - 1) * cosw0 - 2 * asquared * alpha
67+
else:
68+
raise Exception("Must specify 'high' or 'low' for shelf type, instead got: ", type)
69+
6570
return [b0, b1, b2], [a0, a1, a2]
6671

6772

@@ -82,8 +87,14 @@ def __init__(self, fs, dBgain, mid_point, Q=1 / 2):
8287
# TODO:
8388
# We want to generate this based on time constant and 'X' value
8489
# currently we use the mid frequency and a gain to get the correct shape
85-
# with eyeballed mid value. Q=1/2 seems to give the corret slope.
86-
self.ataps, self.btaps = gen_high_shelf(mid_point, dBgain, Q, fs)
90+
# with eyeballed mid value. Q=1/2 seems to give the correct slope.
91+
self.ataps, self.btaps = gen_shelf(
92+
mid_point,
93+
dBgain,
94+
"high",
95+
fs,
96+
qfactor=Q
97+
)
8798

8899
def get(self):
89100
return self.btaps, self.ataps

vhsdecode/compute_video_filters.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from importlib.resources import files
1313

1414
from vhsdecode.utils import filtfft
15-
from vhsdecode.addons.FMdeemph import FMDeEmphasisB, gen_low_shelf, gen_high_shelf
15+
from vhsdecode.addons.FMdeemph import FMDeEmphasisB, gen_shelf
1616

1717
NONLINEAR_AMP_LPF_FREQ_DEFAULT = 700000
1818
NONLINEAR_STATIC_FACTOR_DEFAULT = None
@@ -77,10 +77,10 @@ def gen_custom_video_filters(filter_list, freq_hz, block_len):
7777
file=sys.stderr,
7878
)
7979
case "highshelf":
80-
db, da = gen_high_shelf(f["midfreq"], f["gain"], f["q"], freq_hz / 2.0)
80+
db, da = gen_shelf(f["midfreq"], f["gain"], "high", freq_hz / 2.0, qfactor=f["q"])
8181
ret *= filtfft((db, da), block_len, whole=False)
8282
case "lowshelf":
83-
db, da = gen_low_shelf(f["midfreq"], f["gain"], f["q"], freq_hz / 2.0)
83+
db, da = gen_shelf(f["midfreq"], f["gain"], "low", freq_hz / 2.0, qfactor=f["q"])
8484
ret *= filtfft((db, da), block_len, whole=False)
8585
return ret
8686

0 commit comments

Comments
 (0)