# Main code for meap mashups.

from scipy import *
from stft import *
from onset import *

# real loop:
# while no song input
#  wait for non-silence
# pause 2.2 seconds
# while song input
#   read in 1 audio frame
#   take fft
#   add to stft buffer, bump out last frame
#   detect onset up to last onset
#   if there is an onset
#     extract chunk up to the last onset or end of buffer
#     calculate features of chunk
#     find closest chunk in database
#     add chunk to database (inactive until end of current song)

def reconstruct(source, target, fs, cheat=0, noise=.1):
    '''(yout,subst) = reconstruct(source, target, fs, cheat, noise)
    
    Build one song out of the pieces of another.  Chop both songs up
    into fragments between their onsets, calculate features for each
    chunk, substitute the source chunk that matches each target chunk
    most closely into the time slot of the target chunk.  fs is the
    sample rate of source and target, cheat is a binary flag
    indicating whether to return a second channel in yout of the
    target song for comparison, and noise is the amount of noise to
    add to the distance measure between chunks to make things a little
    more interesting.  The return argument subst indicates the indices
    of the source chunks substituted for each target chunk.
    '''

    frame = 256
    s_ons = onset(source, fs, frame)
    t_ons = onset(target, fs, frame)
    s_feat = calcFeatures(source, s_ons, fs)
    t_feat = calcFeatures(target, t_ons, fs)

    D = dist(s_feat, transpose(t_feat))
    (r,c) = shape(D)
    D = D + noise*rand(r,c) * diag(mean(D))
    subst = argmin(transpose(D))

    # Length of each source segment
    s_len = transpose(diff(transpose(s_ons)))

    yout = zeros(t_ons[-1]+s_len[-1])

    for i in range(size(subst)):
        yout[t_ons[i,0]:t_ons[i,0]+s_len[subst[i]]] = \
            yout[t_ons[i,0]:t_ons[i,0]+s_len[subst[i]]] + \
            window(source[s_ons[subst[i],0]:s_ons[subst[i],1]]);

    return (yout, subst)


def window(x):
    '''multiply the vector x by a triangular windowing function to avoid
    clicks
    '''
    
    l = 50.
    w = r_[arange(0,1,1/l), ones(size(x)-2*l-1), arange(1,0,1/l), 0]
    return w * x


def calcFeatures(y, ons, fs):
    '''Calculate some auditory features of each chunk in the input
    sound.  Ons is a list of onsets as returned by the onset function,
    an Nx2 matrix where ons[i,0] is the beginning of a chunk in
    samples and ons[i,1] is the end of the chunk.  Returns an NxM
    matrix, with each row representing the features for the
    corresponding chunk.
    '''
    
    frame = 32;
    hop = frame/2;
    S = log(absolute(stft(y,frame,frame,hop)))
    ons = around(ons / hop)

    for i in range(size(ons,0)):
        print ons[i,:]
        chunk_S = S[:,ons[i,0]:ons[i,1]]
        if(size(chunk_S) <= 0):
            continue
        F = r_[F,c_[envelope(chunk_S),avg_spec(chunk_S)]]

    return F

        
def envelope(chunk_S):
    '''Calculate the energy envelope of the spectrogram chunk chunk_S
    '''

    n = 16
    s = sum(chunk_S)
    if(size(s) < n):
        s = c_[s,zeros(n-size(s))]
    return s[0:n]


def avg_spec(chunk_S):
    '''Calculate the average spectrum of the spectrogram chunk chunk_S
    '''
    return sum(transpose(chunk_S))


# def onset(y, fs, frame):
#     '''Dummy onset detector, returns chunks that are 1/2 a second long
#     every second
#     '''
#     secs = size(y)/fs
#     starts = arange(0,secs-.5)
#     starts = reshape(starts, (secs,1))
#     print c_[starts,starts+.5]
#     return c_[starts,starts+.5]
