Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
MPAI-Private
MPAI-CAE
arp
Tape Audio Restoration
Commits
8ceb5875
Commit
8ceb5875
authored
Jan 20, 2023
by
Nadir Dalla Pozza
Browse files
Plots removed
parent
e1615572
Changes
3
Hide whitespace changes
Inline
Side-by-side
README.md
View file @
8ceb5875
...
...
@@ -38,11 +38,7 @@ to plot filter frequency response;
3.
`STANDARD_W`
that specifies the equalisation standard used to record the tape;
4.
`SPEED_W`
that specifies the speed used when recording the tape;
5.
`STANDARD_R`
that specifies the equalisation standard used to read the tape;
6.
`SPEED_R`
that specifies the speed used when reading the tape;
7.
`FS`
that specifies the sampling frequency to be used for the filters and the impulse responses.
This parameter won't be considered if
`FILES_NAME`
parameter is not an empty string;
8.
`BPS`
that specifies the bits-per-sample to be used when saving the filter impulse response as WAV;
9.
`PLOTS`
that specifies if the plots of the frequency response should be shown.
6.
`SPEED_R`
that specifies the speed used when reading the tape.
To execute the script without issues, the inner structure of the
`WORKING_PATH`
directory shall be like:
```
...
...
@@ -112,7 +108,7 @@ With this structure, `FILES_NAME` parameter could be equal to `File1` or `File2`
You can now launch the
*Tape Audio Restoration*
from the command line with:
```
python3
mai
n.py
python3
tapeAudioRestoratio
n.py
```
Useful log information will be displayed during execution, requiring occasional interaction.
...
...
config.yaml
View file @
8ceb5875
...
...
@@ -15,7 +15,4 @@ SPEED_W: 7.5
STANDARD_R
:
"
CCIR"
# Reading tape speed [ips].
# Accepted values: (3.75, 7.5, 15, 30)
SPEED_R
:
15
# Enable plotting filter frequency responses
# Accepted values: (true, false)
PLOTS
:
false
\ No newline at end of file
SPEED_R
:
15
\ No newline at end of file
tapeAudioRestoration.py
View file @
8ceb5875
...
...
@@ -10,7 +10,6 @@ It identifies and restore portions of the Preservation Audio File, providing:
"""
import
array
import
matplotlib.pyplot
as
plt
import
numpy
as
np
import
os
import
shutil
...
...
@@ -20,7 +19,7 @@ from argparse import ArgumentParser, RawTextHelpFormatter
from
control
import
c2d
,
TransferFunction
from
numpy
import
ndarray
from
scipy.io
import
wavfile
from
scipy.signal
import
freqs
,
freqz
,
tf2zpk
,
zpk2tf
,
lfilter
from
scipy.signal
import
tf2zpk
,
zpk2tf
,
lfilter
__author__
=
"Nadir Dalla Pozza"
__copyright__
=
"Copyright 2022, Audio Innova S.r.l."
...
...
@@ -48,7 +47,7 @@ class CC:
END
=
'
\033
[0m'
def
get_arguments
()
->
tuple
[
str
,
str
,
str
,
float
,
str
,
float
,
bool
]:
def
get_arguments
()
->
tuple
[
str
,
str
,
str
,
float
,
str
,
float
]:
"""
Method to obtain arguments from config.yaml file or command line.
Default config.yaml, ignored if a command line argument is passed.
...
...
@@ -58,8 +57,7 @@ def get_arguments() -> tuple[str, str, str, float, str, float, bool]:
3) str specifying the equalization standard used when the tape was recorded;
4) float specifying the speed used when the tape was recorded;
5) str specifying the equalization standard used when the tape was read;
6) float specifying the speed used when the tape was read;
7) bool specifying if filter figures should be plotted.
6) float specifying the speed used when the tape was read.
"""
if
len
(
sys
.
argv
)
>
1
:
...
...
@@ -120,12 +118,6 @@ def get_arguments() -> tuple[str, str, str, float, str, float, bool]:
speed_w
=
float
(
args
.
speed_w
)
standard_r
=
args
.
equalization_r
speed_r
=
float
(
args
.
speed_r
)
plots
=
False
if
args
.
plot_figures
in
(
'true'
,
'True'
):
plots
=
True
elif
args
.
plot_figures
not
in
(
'false'
,
'False'
):
print
(
CC
.
RED
+
'Invalid PLOT input argument!'
+
CC
.
END
)
quit
(
os
.
EX_CONFIG
)
else
:
# Read configuration file
config
=
object
...
...
@@ -149,9 +141,6 @@ def get_arguments() -> tuple[str, str, str, float, str, float, bool]:
if
'SPEED_R'
not
in
config
:
print
(
CC
.
RED
+
'SPEED_R key not found in config.yaml!'
+
CC
.
END
)
quit
(
os
.
EX_CONFIG
)
if
'PLOTS'
not
in
config
:
print
(
CC
.
RED
+
'PLOTS key not found in config.yaml!'
+
CC
.
END
)
quit
(
os
.
EX_CONFIG
)
except
FileNotFoundError
:
print
(
CC
.
RED
+
'config.yaml file not found!'
+
CC
.
END
)
quit
(
os
.
EX_NOINPUT
)
...
...
@@ -161,13 +150,7 @@ def get_arguments() -> tuple[str, str, str, float, str, float, bool]:
speed_w
=
config
[
'SPEED_W'
]
standard_r
=
config
[
'STANDARD_R'
]
speed_r
=
config
[
'SPEED_R'
]
plots
=
False
if
config
[
'PLOTS'
]
in
(
True
,
'true'
,
'True'
):
plots
=
True
elif
config
[
'PLOTS'
]
not
in
(
False
,
'false'
,
'False'
):
print
(
CC
.
RED
+
'Invalid PLOT input argument!'
+
CC
.
END
)
quit
(
os
.
EX_CONFIG
)
return
working_path
,
files_name
,
standard_w
,
speed_w
,
standard_r
,
speed_r
,
plots
return
working_path
,
files_name
,
standard_w
,
speed_w
,
standard_r
,
speed_r
def
check_input
(
working_path
:
str
,
files_name
:
str
,
standard_w
:
str
,
speed_w
:
float
,
standard_r
:
str
,
speed_r
:
float
)
->
tuple
[
str
,
str
,
str
,
str
]:
...
...
@@ -698,61 +681,23 @@ def get_correction_filter(standard_w: str, speed_w: float, standard_r: str, spee
return
a
,
b
,
fs
,
case
def
correction
(
a
:
array
,
b
:
array
,
paf
:
ndarray
,
fs
:
int
,
plots
:
bool
)
->
ndarray
:
def
correction
(
a
:
array
,
b
:
array
,
paf
:
ndarray
,
fs
:
int
)
->
ndarray
:
"""
Apply a correction filter to a Preservation Audio File;
:param a: array of coefficients, specifying the numerator of filter transfer function,
:param b: array of coefficients, specifying in the denominator of filter transfer function,
:param paf: ndarray specifying the raw audio data of the Preservation Audio File,
:param fs: int specifying the operational sampling frequency,
:param plots: bool specifying if filter plots should be displayed.
:param fs: int specifying the operational sampling frequency.
:return: the corrected audio as a Restored Audio File.
"""
# Analog transfer function
h_a
=
TransferFunction
(
a
,
b
)
# Analog frequency vector
w_a
=
np
.
logspace
(
np
.
log10
(
1
),
np
.
log10
(
fs
*
np
.
pi
),
5000
)
if
plots
:
# Analog filter frequency response
w_t
,
h_t
=
freqs
(
a
,
b
,
worN
=
w_a
)
# Plot analog graph
# - Magnitude
plt
.
subplot
(
2
,
1
,
1
)
plt
.
semilogx
(
w_t
/
(
2
*
np
.
pi
),
20
*
np
.
log10
(
abs
(
h_t
)))
plt
.
xlim
([
1
,
24000
])
plt
.
xlabel
(
'Frequency'
)
plt
.
ylim
([
-
40
,
40
])
plt
.
ylabel
(
'Amplitude response [dB]'
)
plt
.
grid
(
True
)
# - Phase
plt
.
subplot
(
2
,
1
,
2
)
plt
.
semilogx
(
w_t
/
(
2
*
np
.
pi
),
np
.
angle
(
h_t
)
*
180
/
np
.
pi
)
plt
.
xlim
([
1
,
24000
])
plt
.
xlabel
(
'Frequency'
)
plt
.
ylabel
(
'Phase [deg]'
)
plt
.
grid
(
True
)
# Digital transfer function through bilinear digitisation
h_d
=
c2d
(
h_a
,
1
/
fs
,
'bilinear'
)
num_d
=
h_d
.
num
[
0
][
0
]
# Inspect Hd.num to see why [0][0] is needed...
den_d
=
h_d
.
den
[
0
][
0
]
# Same story here
# Digital frequency vector
w_d
=
np
.
logspace
(
np
.
log10
(
1
),
np
.
log10
(
fs
/
2
),
5000
)
if
plots
:
# Digital filter frequency response
w_n
,
h_n
=
freqz
(
num_d
,
den_d
,
worN
=
w_d
,
fs
=
fs
)
# Plot digital graph
# - Magnitude
plt
.
subplot
(
2
,
1
,
1
)
plt
.
semilogx
(
w_n
,
20
*
np
.
log10
(
abs
(
h_n
)),
'--'
)
plt
.
legend
([
'Analog'
,
'Bilinear'
])
# - Phase
plt
.
subplot
(
2
,
1
,
2
)
plt
.
semilogx
(
w_n
,
np
.
angle
(
h_n
)
*
180
/
np
.
pi
,
'--'
)
plt
.
legend
([
'Analog'
,
'Bilinear'
])
# Pole check
...
...
@@ -772,38 +717,11 @@ def correction(a: array, b: array, paf: ndarray, fs: int, plots: bool) -> ndarra
# Analog transfer function
hp_a
=
TransferFunction
(
ap
,
bp
)
if
plots
:
# Analog filter frequency response
wp_t
,
hp_t
=
freqs
(
ap
,
bp
,
worN
=
w_a
)
# Plot analog graph
# - Magnitude
plt
.
subplot
(
2
,
1
,
1
)
plt
.
semilogx
(
wp_t
/
(
2
*
np
.
pi
),
20
*
np
.
log10
(
abs
(
hp_t
)))
# - Phase
plt
.
subplot
(
2
,
1
,
2
)
plt
.
semilogx
(
wp_t
/
(
2
*
np
.
pi
),
np
.
angle
(
hp_t
)
*
180
/
np
.
pi
)
# Digital transfer function through bilinear digitisation
hp_d
=
c2d
(
hp_a
,
1
/
fs
,
'bilinear'
)
num_d
=
hp_d
.
num
[
0
][
0
]
den_d
=
hp_d
.
den
[
0
][
0
]
if
plots
:
# Digital filter frequency response
wp_n
,
hp_n
=
freqz
(
num_d
,
den_d
,
worN
=
w_d
,
fs
=
fs
)
# Plot digital graph
# - Magnitude
plt
.
subplot
(
2
,
1
,
1
)
plt
.
semilogx
(
wp_n
,
20
*
np
.
log10
(
abs
(
hp_n
)),
'--'
)
plt
.
legend
([
'Analog'
,
'Bilinear'
,
'Pole - Analog'
,
'Pole - Digital'
])
# - Phase
plt
.
subplot
(
2
,
1
,
2
)
plt
.
semilogx
(
wp_n
,
np
.
angle
(
hp_n
)
*
180
/
np
.
pi
,
'--'
)
plt
.
legend
([
'Analog'
,
'Bilinear'
,
'Pole - Analog'
,
'Pole - Digital'
])
if
plots
:
plt
.
show
()
print
(
'
\n
Filtering Preservation Audio File...'
)
# Filter Preservation Audio File
raf
=
lfilter
(
num_d
,
den_d
,
paf
,
axis
=
0
)
...
...
@@ -855,7 +773,7 @@ def main():
print
(
"You are using Python version: "
+
sys
.
version
)
# Get the input from config.yaml or command line
working_path
,
files_name
,
standard_w
,
speed_w
,
standard_r
,
speed_r
,
plots
=
get_arguments
()
working_path
,
files_name
,
standard_w
,
speed_w
,
standard_r
,
speed_r
=
get_arguments
()
# Check if input is correct
paf_path
,
temp_path
,
standard_w
,
standard_r
=
check_input
(
working_path
,
files_name
,
standard_w
,
speed_w
,
standard_r
,
speed_r
)
...
...
@@ -885,7 +803,7 @@ def main():
# Correction phase
if
len
(
a
)
!=
0
:
# Not all cases present a correction filter!
raf
=
correction
(
paf
,
a
,
b
,
fs
,
plots
)
raf
=
correction
(
paf
,
a
,
b
,
fs
)
save_file
(
raf
,
fs
,
temp_path
,
'1'
)
else
:
# Just save Restored Audio File, but with modified fs
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment