/*************************************************************************/
/*                                                                       */
/*                  Language Technologies Institute                      */
/*                     Carnegie Mellon University                        */
/*                         Copyright (c) 1999                            */
/*                        All Rights Reserved.                           */
/*                                                                       */
/*  Permission is hereby granted, free of charge, to use and distribute  */
/*  this software and its documentation without restriction, including   */
/*  without limitation the rights to use, copy, modify, merge, publish,  */
/*  distribute, sublicense, and/or sell copies of this work, and to      */
/*  permit persons to whom this work is furnished to do so, subject to   */
/*  the following conditions:                                            */
/*   1. The code must retain the above copyright notice, this list of    */
/*      conditions and the following disclaimer.                         */
/*   2. Any modifications must be clearly marked as such.                */
/*   3. Original authors' names are not deleted.                         */
/*   4. The authors' names are not used to endorse or promote products   */
/*      derived from this software without specific prior written        */
/*      permission.                                                      */
/*                                                                       */
/*  THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK        */
/*  DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING      */
/*  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT   */
/*  SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE     */
/*  FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES    */
/*  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN   */
/*  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,          */
/*  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF       */
/*  THIS SOFTWARE.                                                       */
/*                                                                       */
/*************************************************************************/
/*                 Author: Alan W Black                                  */
/*                   Date: Dec 2000                                      */
/* --------------------------------------------------------------------- */
/*  Some more interesting join methods                                   */
/*                                                                       */
/*************************************************************************/

#include "EST_error.h"
#include "us_synthesis.h"

static int awb_voiced(EST_Track &pm,int i)
{
    // Hacky guess at voicedness
    return TRUE;
    if ((i < 2) || (i+3 > pm.num_frames()))
	return FALSE;
    else if (((pm.t(i) - pm.t(i-1)) == (pm.t(i+1) - pm.t(i))) &&
	     ((pm.t(i-1) - pm.t(i-2)) == (pm.t(i) - pm.t(i-1))))
	return FALSE;
    else
	return TRUE;
}

static float frame_duration(EST_Track &pm, int i)
{
    if (i <= 0)
	return frame_duration(pm,i+1);
    else if (i >= pm.num_frames())
	return frame_duration(pm,pm.num_frames()-1);
    else
	return pm.t(i)-pm.t(i-1);
}

static float awb_smoothed(EST_Track &pm, int i)
{
    // Returned smoothed pitch period at i

    return (frame_duration(pm,i-4)+
	    frame_duration(pm,i-3)+
	    frame_duration(pm,i-2)+
	    frame_duration(pm,i-1)+
	    frame_duration(pm,i)+
	    frame_duration(pm,i+1)+
	    frame_duration(pm,i+2))/7.0;
}

void make_segment_varied_mapping(EST_Relation &source_lab, 
				 EST_Track &source_pm, 
				 EST_Track &target_pm, 
				 EST_IVector &map) 
{
    int i = 0;
    int spp;
    float ptime,stime,ttime,sstime;
    int s_i_start, s_i_end, t_i_start, t_i_end;
    EST_Item *s;
    float s_end, s_start, t_end, t_start, f, m;
    map.resize(target_pm.num_frames());
    EST_Track ntarget_pm;
    
    s_start = t_start = 0.0;
    ntarget_pm = target_pm;

    if (target_pm.t(target_pm.num_frames() - 1) < 
	source_lab.tail()->F("end",0))
    {
	EST_warning("Target pitchmarks end before end of target segment "
		    "timings (%f vs %f). Expect a truncated utterance\n",
		    target_pm.t(target_pm.num_frames() - 1),
	            source_lab.tail()->F("end",0.0));
    }

    for (s = source_lab.head(); s; s = next(s))
    {
	s_end = s->F("source_end");
	t_end = s->F("end");
	
	s_i_start = source_pm.index_below(s_start);
	s_i_end = source_pm.index_below(s_end);
	t_i_start = target_pm.index_below(t_start);
	t_i_end = target_pm.index_below(t_end);

	// fudge to make sure that at least one frame is available
	if (s_i_end <= s_i_start)
	    s_i_end += 1;
	
	m = float (s_i_end - s_i_start)/ float(t_i_end - t_i_start);
	for (i = t_i_start, f = 0.0; i < t_i_end; ++i, ++f)
	{
	    spp = EST_NINT(f * m) + s_i_start;
	    if (spp == 0)
		stime = source_pm.t(spp);
	    else
		stime = source_pm.t(spp) - source_pm.t(spp-1);
	    // modified if voiced, unmodified if unvoiced
	    if (i == 0)
		ttime = target_pm.t(i);
	    else
		ttime = target_pm.t(i) - target_pm.t(i-1);
	    if (awb_voiced(source_pm,i))
	    {
		sstime = awb_smoothed(source_pm,spp);
		ptime = stime + (0.2*(ttime-sstime));
//		ptime = stime;
	    }
	    else 
		ptime = stime; 
	    ntarget_pm.t(i) = ntarget_pm.t(i-1) + ptime;
	    printf("t(%d) = %f dur %f stime %f ttime %f factor %f\n",
		   i,ntarget_pm.t(i),ptime,stime,ttime,
		   (ttime-stime)/stime);
	    
            map[i] = EST_NINT(f * m) + s_i_start;
	}

	s_start = s->F("source_end");
	t_start = s->F("end");
    }

    ntarget_pm.resize(i,ntarget_pm.num_channels());

    printf("target_pm.end() = %f ntarget_pm.end() = %f\n",
	   target_pm.end(), ntarget_pm.end());
    target_pm = ntarget_pm;
    printf("target_pm.end() = %f ntarget_pm.end() = %f\n",
	   target_pm.end(), ntarget_pm.end());
    if (i == 0)
	map.resize(0);  // nothing to synthesize
    else
	map.resize(i - 1);
}

void nn_mapping(EST_Utterance &utt, const EST_String &method)
{
    EST_Relation *source_lab, *target_lab;
    EST_IVector *map;
    EST_Track *source_coef=0, *target_coef=0;

    source_coef = track(utt.relation("SourceCoef")->head()->f("coefs"));
    target_coef = track(utt.relation("TargetCoef")->head()->f("coefs"));
    
    map = new EST_IVector;
    
//    cout << "mapping method: " << method << endl;
    if (method != "segment_single")
	source_lab = utt.relation("SourceSegments");
    target_lab = utt.relation("Segment", 1);

/*    if (method == "segment")
	make_segment_double_mapping(*source_lab, *source_coef, *target_lab, 
			     *target_coef, *map);
    else if (method == "dp_segment")
	make_dp_mapping(*source_lab, *source_coef, *target_lab, 
			     *target_coef, "Match", *map);
			     */
    make_segment_varied_mapping(*target_lab, *source_coef,
				*target_coef, *map);

    utt.create_relation("US_map");
    EST_Item *item = utt.relation("US_map")->append();
    item->set_val("map", est_val(map));

//    cout << "map: ";
//    for (int i = 0; i < map->n(); ++ i)
//	cout << i << ":" << map->a(i) << "  ";
//    cout << endl;
      
}

LISP l_nn_mapping(LISP utt, LISP method)
{
    EST_Utterance *u = get_c_utt(utt);
    EST_String s = get_c_string(method);

    nn_mapping(*u,s);

    return utt;
}



