#!/usr/bin/env python

"""Duration estimator for speech.

It takes a local spectrum,
bins it onto the Bark scale,
converts to perceptual loudness (via **E).
Then, it computes a measure of how far you can go
from each point before the spectrum changes too much.
"""

import numpy
from gmisclib import cache as CC
from gmisclib import erb_scale

from gpk_voicing import cached_ps as PSC
from gpk_voicing import percep_spec as PS
from gpk_voicing import fv_misc as FVM
SillyWidthException = FVM.SillyWidthException
NSV = 0.75

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

def feature_vec_guts(data, dt, Dt, do_irx=True,
		LF=1.0, Nsv=NSV, cache_info=None
		):
	FORMANT_LOW = erb_scale.f_to_erb(200.0)
	FORMANT_HIGH = erb_scale.f_to_erb(4000.0)
	assert Dt > 0.0 and float(Dt)>0.0
	bmin = erb_scale.f_to_erb(100.0)
	bmax = erb_scale.f_to_erb(6000.0)
	# all_ectrs, all_ps, t0 = PS.perceptual_spec(data, dt, Dt, ...
	if cache_info is not None:
		assert isinstance(cache_info, CC.cache_info)
		all_ectrs, all_ps, t0 = PSC.perceptual_spec(data, dt, Dt,
							bmin=bmin, bmax=bmax, db=FVM.DB,
							do_mod=0,
							do_dissonance=False,
							PlompBouman=False,
							cache_info=cache_info.copy()
							)
	else:
		all_ectrs, all_ps, t0 = PS.block_percep_spec(data, dt, Dt,
							bmin=bmin, bmax=bmax, db=FVM.DB,
							do_mod=0,
							do_dissonance=False,
							PlompBouman=False
							)

	band_indices = [i for (i,ec) in enumerate(all_ectrs) if ec['type']=='band']
	neural = all_ps.take(band_indices, axis=0)
	ectrs = [ec for ec in all_ectrs if ec['type']=='band']
	nband_indices = [i for (i,ec) in enumerate(all_ectrs) if ec['type']!='band']
	nneural = all_ps.take(nband_indices, axis=0)
	nectrs = [ec for ec in all_ectrs if ec['type']!='band']

	assert nneural.shape[1]==neural.shape[1]
	assert neural.shape[1]==all_ps.shape[1]
	assert neural.shape[0]+nneural.shape[0] == all_ps.shape[0]
	assert len(data.shape) == 1
	assert abs(all_ps.shape[1]*Dt-data.shape[0]*dt) < 0.1*data.shape[0]*dt

	neural_now = numpy.average(neural, axis=0)
	assert neural_now.shape[0] == neural.shape[1]
	neural_avg = numpy.square(neural_now).sum()/numpy.sum(neural_now)
	numpy.divide(neural, neural_avg, neural)
	numpy.divide(neural_now, neural_avg, neural_now)

	assert nneural.shape[0] < nneural.shape[1]
	assert len(nectrs) == nneural.shape[0]
	for (i,e) in enumerate(nectrs):
		assert e['type'] == 'band'

	# print '# neural_avg=', neural_avg
	w = 0.04 * LF / Dt
	if do_irx:
		w = _irx(w)
	o, descr = FVM.vowel(w, ectrs, neural, neural_now, Nsv,
						formant_low=FORMANT_LOW,
						formant_high=FORMANT_HIGH
						)
	assert len(descr)==len(o), "Descriptor mismatch"
	return (o, descr, Dt, t0)


def feature_vec(data, dt, DT, LF=1.0, Nsv=NSV, do_irx=True, cache_info=None):
	o = None
	ci = None
	if cache_info is not None:
		assert isinstance(cache_info, CC.cache_info)
		ci = cache_info.addinfo(dt, DT, LF, Nsv, 'fv_pdur')
		try:
			o = ci.load()
		except ci.Errors:
			pass
	if o is None:
		o = feature_vec_guts(data, dt, DT, LF=LF, Nsv=Nsv,
					cache_info=cache_info
					)
		if ci is not None:
			ci.bg_dump(o)
	return o
