mirror of https://github.com/drowe67/phasenn.git
168 lines
5.1 KiB
Python
Executable File
168 lines
5.1 KiB
Python
Executable File
#!/usr/bin/python3
|
|
# plot_n0.py
|
|
#
|
|
# David Rowe Dec 2019
|
|
|
|
# Generate some plots Plot n0 estimates on top of speech waveforms for a few test frames
|
|
# to see if n0 estimation is working.
|
|
|
|
'''
|
|
Sample usage for a contrived impulse train signal:
|
|
|
|
Generate an impulse train with time offset n0 1, and extract codec 2 model records:
|
|
~codec/build_linux$ ./misc/timpulse 1 | ./src/c2sim - --modelout ~/phasenn/imp.model
|
|
Estimate n0:
|
|
~codec/build_linux$ ./misc/timpulse 1 | ./src/c2sim - --modelout - | ./misc/est_n0 > ~/phasenn/imp_n0.txt
|
|
Remove linear phase component due to time offset n0:
|
|
~codec/build_linux$ ./misc/timpulse 1 | ./src/c2sim - --modelout - | ./misc/est_n0 -r > ~/phasenn/imp_nolinear.model
|
|
|
|
Usage (this script removes linear):
|
|
~phasenn$ ./plot_n0.py imp.model -n0 imp_n0.txt
|
|
Usage (linear removed by C program):
|
|
~phasenn$ ./plot_n0.py imp_nolinear.model
|
|
Usage (start plots at frame 30):
|
|
~phasenn$ ./plot_n0.py --start 30 imp_nolinear.model
|
|
|
|
Green line (phase_n0_removed) should be flat, as the phase of an
|
|
impulse train is comprised of just a linear time shift component, and 0
|
|
dispersive component.
|
|
|
|
'''
|
|
|
|
import numpy as np
|
|
import sys
|
|
import matplotlib.pyplot as plt
|
|
from scipy import signal
|
|
import codec2_model
|
|
import argparse
|
|
import os
|
|
|
|
# constants
|
|
|
|
N = 80 # number of time domain samples in frame
|
|
width = 256
|
|
Fs = 8000
|
|
|
|
parser = argparse.ArgumentParser(description='Plot phase spectra and synthesised speech')
|
|
parser.add_argument('modelfile', help='Codec 2 model file')
|
|
parser.add_argument('--n0file', help='text file of n0 estimates')
|
|
parser.add_argument('--start', type=int, default=30, help=' start frame')
|
|
parser.add_argument('--png', action='store_true')
|
|
|
|
args = parser.parse_args()
|
|
# read in model file records
|
|
Wo, L, A, phase, voiced = codec2_model.read(args.modelfile)
|
|
nb_samples = Wo.size;
|
|
amp = 20.0*np.log10(A+1E-6)
|
|
|
|
# read in n0 estimates
|
|
have_n0 = 0
|
|
if args.n0file:
|
|
n0_est = np.loadtxt(args.n0file)
|
|
have_n0 = 1
|
|
print(n0_est[:10])
|
|
|
|
'''
|
|
# Python version of est_n0.c
|
|
n0_est2 = np.zeros((nb_samples))
|
|
print("estimating linear phase component...")
|
|
for i in range(nb_samples):
|
|
err_min = 1E32
|
|
P = 2*np.pi/Wo[i]
|
|
|
|
for test_n0 in np.arange(0,P,0.25):
|
|
e = np.exp(1j*test_n0*np.arange(1,L[i]+1)*Wo[i])
|
|
err = np.dot(np.log10(A[i,1:L[i]+1]), np.abs(np.exp(1j*phase[i,1:L[i]+1]) - e)**2)
|
|
if err < err_min:
|
|
err_min = err
|
|
n0_est2[i] = test_n0
|
|
print(n0_est2[:10])
|
|
'''
|
|
|
|
if have_n0:
|
|
print("removing linear phase component....")
|
|
phase_linear = np.zeros((nb_samples, width))
|
|
phase_n0_removed = np.zeros((nb_samples, width))
|
|
for i in range(nb_samples):
|
|
for m in range(1,L[i]+1):
|
|
phase_linear[i,m] = n0_est[i]*Wo[i]*m
|
|
phase_n0_removed[i,m] = phase[i,m] - phase_linear[i,m]
|
|
phase_n0_removed[i,m] = np.angle(np.exp(1j*phase_n0_removed[i,m]))
|
|
|
|
# synthesise time domain signal
|
|
def sample_time(r):
|
|
s = np.zeros(2*N);
|
|
|
|
for m in range(1,L[r]+1):
|
|
s = s + A[r,m]*np.cos(m*Wo[r]*range(-N,N) + phase[r,m])
|
|
return s
|
|
|
|
nb_plots = 4; nb_plotsy = np.floor(np.sqrt(nb_plots)); nb_plotsx=nb_plots/nb_plotsy;
|
|
startframe = args.start
|
|
frame = range(startframe,startframe+nb_plots)
|
|
|
|
plt.figure(1)
|
|
plt.title('Amplitudes Spectra')
|
|
for r in range(nb_plots):
|
|
plt.subplot(nb_plotsy,nb_plotsx,r+1)
|
|
f = frame[r];
|
|
plt.plot(amp[f,1:L[f]],'g')
|
|
plt.show(block=False)
|
|
|
|
plt.figure(2)
|
|
plt.title('Phase Spectra')
|
|
for r in range(nb_plots):
|
|
plt.subplot(nb_plotsy,nb_plotsx,r+1)
|
|
f = frame[r]
|
|
if have_n0:
|
|
plt.plot(phase[f,1:L[f]]*180/np.pi,'g')
|
|
plt.plot(np.angle(np.exp(1j*phase_linear[f,1:L[f]]))*180/np.pi,'b')
|
|
plt.plot(phase_n0_removed[f,1:L[f]]*180/np.pi,'r')
|
|
else:
|
|
plt.plot(phase[f,1:L[f]]*180/np.pi,'r')
|
|
plt.ylim(-180,180)
|
|
plt.show(block=False)
|
|
if have_n0:
|
|
print("Green: input phase, Blue: est linear, Red: phase with linear removed")
|
|
else:
|
|
print("Red: phase with linear removed")
|
|
|
|
plt.figure(3)
|
|
plt.title('Time Domain')
|
|
for r in range(nb_plots):
|
|
plt.subplot(nb_plotsy,nb_plotsx,r+1)
|
|
f = frame[r];
|
|
s = sample_time(f)
|
|
plt.plot(range(-N,N),s,'g')
|
|
mx = np.max(np.abs(s))
|
|
# so our centre is n0_est in advance, actual n0 is n0_est behind centre. I think.
|
|
if have_n0:
|
|
plt.plot([-n0_est[f],-n0_est[f]], [-mx/2,mx/2],'b')
|
|
else:
|
|
plt.plot([0,0], [-mx/2,mx/2],'b')
|
|
|
|
plt.show(block=False)
|
|
|
|
print("Click on last figure to finish....")
|
|
plt.waitforbuttonpress(0)
|
|
plt.close()
|
|
|
|
# optionally generate some PNGs to flip though
|
|
|
|
if args.png:
|
|
png_basename = os.path.basename(args.modelfile)
|
|
png_basename = os.path.splitext(png_basename)[0]
|
|
for r in range(nb_plots):
|
|
fig, ax1 = plt.subplots()
|
|
f = frame[r];
|
|
freq = range(1,L[f])*Wo[f]*Fs/(2*np.pi)
|
|
ax1.plot(freq, amp[f,1:L[f]],'g')
|
|
ax1.set_ylabel('amp', color='g')
|
|
ax2 = ax1.twinx()
|
|
ax2.plot(freq,phase[f,1:L[f]]*180/np.pi, 'r')
|
|
ax2.set_ylabel('phase', color='r')
|
|
png_name = "%s_%02d.png" % (png_basename,f)
|
|
print("Writing %s" % (png_name))
|
|
plt.savefig(png_name)
|
|
|