#!/usr/bin/env python

"""Feature vectors for optimized DTW alignment.
Exemplar project with Ladan Baghai-Ravary.
"""

import math
import numpy
from gmisclib import die
from gmisclib import erb_scale
# from gpk_voicing import percep_spec as PS
from gpk_voicing import cached_ps as PS
from gpk_voicing import fv_misc as M
from gpk_voicing import zero as Z
SillyWidthException = M.SillyWidthException

DB = 0.85

Opt_text="""# Trigger="run_to_bottom finished"
# Trigger match
# len(currentlist)= 198 tail= 0.0 nsamp= 198
# max(log_adv,,-fv200909opt_0.av) = 43.27
#  samples used = 198 filename= log_adv,,-fv200909opt_0.av
43.27 logp
# n= 44
1.73693 dist,alpha
2.13363 dist,beta
1.08861 len_exp
1.55219 lf,elf
0.154333 lf,lf
0.140556 nsv
3.53085 scale,fricative%Sedge:.*:13.0-18.8
 2.8547 scale,fricative%Sedge:.*:18.8-23.0
3.30231 scale,fricative%Sedge:.*:2.5-13.0
-3.87654 scale,fricative%Sedge:.*:23.0-27.1
-0.0238129 scale,fricative%Sedge:.*:27.1-30.7
-2.76415 scale,haspitch1:.*
-3.13785 scale,peakalign1:.*
-2.65753 scale,vowel:.*:10.4
2.32077 scale,vowel:.*:11.3
-1.12553 scale,vowel:.*:12.1
0.0529135 scale,vowel:.*:13.0
-0.568936 scale,vowel:.*:13.8
0.622019 scale,vowel:.*:14.7
0.18557 scale,vowel:.*:15.5
1.24299 scale,vowel:.*:16.4
-3.96151 scale,vowel:.*:17.2
0.793534 scale,vowel:.*:18.1
0.0448047 scale,vowel:.*:18.9
1.33993 scale,vowel:.*:19.8
2.00955 scale,vowel:.*:20.6
-0.858686 scale,vowel:.*:21.5
3.48095 scale,vowel:.*:22.3
-3.47638 scale,vowel:.*:23.2
-0.966758 scale,vowel:.*:24.0
-1.98064 scale,vowel:.*:24.9
2.39958 scale,vowel:.*:25.7
0.406809 scale,vowel:.*:26.6
2.68029 scale,vowel:.*:27.4
-2.71892 scale,vowel:.*:28.3
0.715675 scale,vowel:.*:29.1
 1.1363 scale,vowel:.*:30.0
-3.78759 scale,vowel:.*:4.5
1.14752 scale,vowel:.*:5.3
-0.108174 scale,vowel:.*:6.2
-1.18802 scale,vowel:.*:7.0
-0.0958434 scale,vowel:.*:7.9
0.643672 scale,vowel:.*:8.7
2.52746 scale,vowel:.*:9.6
"""
Scale = M.scale_xform(Opt_text,
			name="299010 optimization in /proj/exemplar/ItakuraSaitoDistance/IS-DTW/log_adv,,-fv200909opt_0.av"
			)

def _irx(a):
	return max(1, int(round(a)))


def feature_vec(data, dt, DT,
		LF=1.0, Nsv=M.NSV, ELF=1.0,
		do_voicing=1, do_dissonance=False,
		PlompBouman=False, do_pdur=False,
		zerosub=0.0, do_irx=True, cachename=None):
	assert not do_pdur and not do_dissonance
	FORMANT_LOW = erb_scale.f_to_erb(120.0)
	FORMANT_HIGH = erb_scale.f_to_erb(6000.0)
	assert float(DT)>0.0
	assert float(LF)>0.0
	bmin = erb_scale.f_to_erb(80.0)
	bmax = erb_scale.f_to_erb(6000.0)
	ectrs, neural, t0 = PS.perceptual_spec(data, dt, DT,
							bmin, bmax, DB,
							do_mod=do_voicing,
							do_dissonance=do_dissonance,
							do_peakalign=True,
							PlompBouman=PlompBouman,
							cachename=cachename
							)

	assert 1 < neural.shape[0] < 200
	band_indices = [i for (i,ec) in enumerate(ectrs) if ec['type']=='band']
	neural_b = neural.take(band_indices, axis=0)
	assert neural_b.shape[1]==neural.shape[1]
	if zerosub != 0.0:
		neural_b = neural_b.copy()
		try:
			z = Z.PS(neural_b)
		except Z.ZeroProblem:
			die.warn("Bad utterance: zero does not converge.")
			z = numpy.zeros((neural_b.shape[0],))
		assert neural_b.shape[0]==z.shape[0]
		numpy.subtract(neural_b, z[:,numpy.newaxis], neural_b)
	assert neural_b.shape[1]==neural.shape[1]
	assert neural_b.shape[0]<=neural.shape[0]

	neural_now = numpy.average(neural_b, axis=0)	# Average over frequency.
	assert neural_now.shape[0] == neural.shape[1]
	neural_avg = numpy.sum(neural_now**2)/numpy.sum(neural_now)	# Average over time.
		# neural_avg is a scalar, grand average.
	numpy.divide(neural, neural_avg, neural)
		# Now, we've normalized by an over-all average loudness.
	numpy.divide(neural_now, neural_avg, neural_now)
		# Now, we've normalized by an over-all average loudness.

	for (i,e) in enumerate(ectrs):
		if e['type'] == 'haspitch':
			numpy.divide(neural[i,:], neural_avg, neural[i,:])
		if e['type'] == 'dissonance':
			numpy.divide(neural[i,:], neural_avg, neural[i,:])
		if e['type'] == 'peakalign':
			numpy.divide(neural[i,:], neural_avg**2, neural[i,:])

	if do_irx:
		irx = _irx
	else:
		irx = lambda x: x


	o = []
	descr = []
	w = irx(0.04*LF/DT)
	tmpo, tmpd = M.vowel(w, ectrs, neural, neural_now, Nsv,
						formant_low=FORMANT_LOW,
						formant_high=FORMANT_HIGH
						)
	o.extend(tmpo)
	descr.extend(tmpd)
	assert len(descr)==len(o), "Descriptor mismatch"

	w = irx(0.04*ELF/DT)
	tmpo, tmpd = M.fricative_edge(w, ectrs, neural, neural_now, Nsv,
						do_abs=False
						)
	o.extend(tmpo)
	descr.extend(tmpd)
	assert len(descr)==len(o), "Descriptor mismatch"

	if do_voicing:
		w = irx(0.02*math.sqrt(LF)/DT)
		tmpo, tmpd = M.haspitch(w, ectrs, neural, neural_now, Nsv)
		o.extend(tmpo)
		descr.extend(tmpd)
	assert len(descr)==len(o), "Descriptor mismatch"

	w = irx(0.03*ELF/DT)
	tmpo, tmpd = M.peakiness(w, ectrs, neural, neural_now, Nsv)
	o.extend(tmpo)
	descr.extend(tmpd)
	assert len(descr)==len(o), "Descriptor mismatch"

	N = neural[0].shape[0]
	for (i, (tmp, dsc)) in enumerate(zip(o, descr)):
		assert tmp.shape == (N,), "Wrong size: %d, %s = %d vs. %d" % (i, str(dsc), tmp.shape[0], N)

	return (o, descr, DT, t0)

