#!/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
from gpk_voicing import fv_misc as M
from gpk_voicing import zero as Z
SillyWidthException = M.SillyWidthException

DB = 0.85

# Scale = M.scale_xform(Opt_text,
			# name="299010 optimization in /proj/exemplar/ItakuraSaitoDistance/IS-DTW/log_adv,,-fv200909opt_0.av"
			# )



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):
	assert not do_pdur and not do_dissonance
	FORMANT_LOW = erb_scale.f_to_erb(120.0)
	FORMANT_HIGH = erb_scale.f_to_erb(5000.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 = percep_spec.perceptual_spec(data, dt, DT,
							bmin, bmax, DB,
							do_mod=do_voicing,
							do_dissonance=do_dissonance,
							do_peakalign=True,
							PlompBouman=PlompBouman
							)

	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.percep_spec_zero(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,:])

	# print '# neural_avg=', neural_avg
	o = []
	descr = []
	w = 0.04*LF/DT
	tmpo, tmpd = M.vowel_edge(w, ectrs, neural, neural_now, Nsv,
						do_abs=False,
						formant_low=FORMANT_LOW,
						formant_high=FORMANT_HIGH
						)
	o.extend(tmpo)
	descr.extend(tmpd)
	assert len(descr)==len(o), "Descriptor mismatch"

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

	if do_voicing:
		w = 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 = 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)

