#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""Ejemplo de uso:
#importar el paquete principal
import base

#crear una red a partir del directorio
r=base.net('../datos')
o
r=base.net2('../datos')

#guardar la red
base.ffnet.savenet(r, 'vocales1a.net')

#comprobar predicciones de un directorio
base.test('../datos/test','vocales1a.net')

#reconocer un archivo
base.reconocer('../datos/a1.wav','vocales1a.net')
o
base.reconocer2('../datos/a1.wav','vocales2.net')

#dibujar los inputs
base.plot_agrupado('../datos/a1.wav')

#dibujar el espectro
base.plot_fft('../datos/a1.wav')
"""

import os

import scipy as sci
from pylab import plot,stem,bar

import ffnet
import wav_array

def plot_fft(archivo,fmax=5000,ventana=None):
    """Dibuja la fft del archivo indicado has la frecuencia fmax"""
    data,f,bits=wav_array.wavread(archivo)
    x,y=espectro(data,f,fmax,ventana)
    plot(x,y)
    return x,y

def espectro(data,f,fmax=5000,ventana=None):
    """Devuelve frecuencia,amplitud para el espectro de los datos en el dominio
    de la frecuencia hasta la frecuencia fmax
    f es la frecuencia de muestreo
    ventanas: None, sci.signal.signaltools.bartlett, etc. """
    if ventana is not None:
        data=data*ventana(len(data))
    y=abs(sci.fft(data))[:len(data)/2]
    x=sci.arange(len(y))*float(f)/len(y)
    i=[i for i in range(len(x)) if fmax<=x[i]][0]
    return x[:i],y[:i]

def agrupar(espectro,rango=20):
    """Agrupa la energia en el entorno de los valores multiplos de 100, con rango dado"""
    x,y=espectro
    rang=sci.arange(0,x[-1],100)
    salida=sci.zeros(len(rang))
    for i in range(len(rang)):
        ind=x.searchsorted(rang[i])
        salida[i]=sci.sum(y[ind-rango:ind+rango])
    salida=salida/sci.sum(salida)
    return rang,salida
    
def plot_agrupado(archivo,fmax=5000,rango=20):
    """Dibuja el espectro del archivo indicado hasta una frecuencia maxima de fmax
    agrupando los datos en multiplos de 100"""
    data,f,bits=wav_array.wavread(archivo)
    x,y=agrupar(espectro(data,f,fmax),rango)
    bar(x,y,width=50)
    #stem(x,y,'b.-','b.')
    #plot(x,y)

def crearInput(archivo):
    """Devuelve el espectro agrupado del archivo (ver agrupar)"""
    fmax=5000
    data,f,bits=wav_array.wavread(archivo)
    x,y=agrupar(espectro(data,f,fmax),rango=50)
    return y

def crearInputTarget(dir):
    """Devuelve los inputs: espectro agrupado de un archivo
    y los targets: 0-4 (identificando las vocales a-u)
    para cada archivo del directorio dir.
    Los archivos tienen que tener el formato v*.wav
    donde v es la vocal que representan"""
    letras={'a':0,'e':1,'i':2,'o':3,'u':4}
    input=[]
    target=[]
    direc=[x for x in os.listdir(dir) if x.endswith('wav')]
    direc.sort(lambda x,y:cmp(x.split('.')[0][1:],y.split('.')[0][1:]))
    for arch in direc:
        input.append(crearInput('/'.join([dir,arch])))
        target.append(letras[arch[0]])
    return input,target

def red(dir):
    """Crea y devuelve una red neuronal entrenada con los archivos de vocales del 
    directorio dir.
    El directorio dir tiene que tener archivos con formato v*.wav
    donde v es la vocal que representan
    y no debe contener otros archivos .wav"""
    con=ffnet.mlgraph((50,10,10,1))
    net=ffnet.ffnet(con)
    input,target=crearInputTarget(dir)
    net.train_tnc(input, target, maxfun = 1000)
    net.test(input, target, iprint = 2)
    return net

def red2(dir):
    """Crea y devuelve una red neuronal entrenada con los archivos de vocales del 
    directorio dir.
    El directorio dir tiene que tener archivos con formato v*.wav
    donde v es la vocal que representan
    y no debe contener otros archivos .wav
    Version 2, con salida de 5 neuronas, representando las 5 vocales"""
    dic={0:[1,0,0,0,0],1:[0,1,0,0,0],2:[0,0,1,0,0],3:[0,0,0,1,0],4:[0,0,0,0,1]}
    con=ffnet.mlgraph((50,10,10,5))
    net=ffnet.ffnet(con)
    input,target=crearInputTarget(dir)
    target=[dic[x] for x in target]
    net.train_tnc(input, target, maxfun = 1000)
    net.test(input, target, iprint = 2)
    return net
        
def reconocer(archivo,archivo_red='vocales.net'):
    """Usa la red entrenada que se encuentra en el directorio de sources
    llamada vocales.net para reconocer la vocal del archivo indicado"""
    if os.path.exists(archivo_red):
        net=ffnet.loadnet(archivo_red)
    else:
        net=ffnet.loadnet(os.path.dirname(wav_array.__file__)+'/'+archivo_red)
    input=crearInput(archivo)
    letras=['a','e','i','o','u']
    return letras[int(round(net(input)[0]))]
   
def reconocer2(archivo,archivo_red='vocales2.net'):
    """Usa la red entrenada que se encuentra en el directorio de sources
    llamada vocales.net para reconocer la vocal del archivo indicado"""
    if os.path.exists(archivo_red):
        net=ffnet.loadnet(archivo_red)
    else:
        net=ffnet.loadnet(os.path.dirname(wav_array.__file__)+'/'+archivo_red)
    input=crearInput(archivo)
    letras=['a','e','i','o','u']
    return [round(100*x)/100. for x in net(input)]

def test(dir,archivo_red='vocales.net'):
    """Comprueba la prediccion de la red con todo los archivo del directorio con extension wav"""
    input,target=crearInputTarget(dir)
    if '2' in archivo_red:
        dic={0:[1,0,0,0,0],1:[0,1,0,0,0],2:[0,0,1,0,0],3:[0,0,0,1,0],4:[0,0,0,0,1]}
        target=[dic[x] for x in target]
    else:
        target=[[x] for x in target]
    
    net=ffnet.loadnet(os.path.dirname(wav_array.__file__)+'/'+archivo_red)
    ok=0
    for i in range(len(input)):
        prediccion=[round(100*x)/100. for x in net(input[i])]
        print target[i],prediccion
        #print map(lambda x,y:abs(x-y),target[i],prediccion)
        if sci.all(map(lambda x,y:abs(x-y)<0.5,target[i],prediccion)):
            ok+=1
    print 'porcentaje correcto: ',ok*100./len(input),'%'

def sexo(archivo):
    """Determina el sexo de la persona que ha pronunciado la vocal del archivo"""
    data,f,bits=wav_array.wavread(archivo)
    x,y=espectro(data,f,300)
    fminh=[n for n in range(len(x)) if x[n]>100][0]
    fmaxh=[n for n in range(len(x)) if x[n]<160][-1]
    fminm=[n for n in range(len(x)) if x[n]>170][0]
    fmaxm=[n for n in range(len(x)) if x[n]<230][-1]
    hombre=max(y[fminh:fmaxh])
    mujer=max(y[fminm:fmaxm])
    if hombre>=mujer:
        return 'hombre'
    return 'mujer'

