view equalLoudness.py @ 11:ca98f5f26bcb tip

* More frequencies to verify toolbox with.
author Carl Bussey <c.bussey@se10.qmul.ac.uk>
date Tue, 22 Apr 2014 16:32:07 +0100
parents c5e7162fb8ea
children
line wrap: on
line source
import pyphon as pp
import numpy as np
import matplotlib.pyplot as plt
import pdb
import scipy.optimize as opt
import scipy.interpolate as interp

fArray = [20, 50, 100, 200, 400, 800, 1000, 1500, 2000, 3000, 4000, 5000, 6000, 10000, 12000, 15000]
#dArray = np.array([5, 10, 50, 100, 150, 200, 500, 1000])/1000.0
dArray = np.array([8, 16, 32, 64, 128, 256, 512, 1024])/1000.0

def get_elc_range(xtol=0.0000005, verbose=False):
    """
        Plot the prediction of the equal loudness contours of frequencies for a range of SPL levels from 10 to 120dB, intervals of 10dB.
        
        Parameters:
            * xtol (type: float) - x tolerance of fsolve
            * verbose (type: boolean) - I tell you what I know
        
        Returns:
            * elc (type: numpy array of floats) - SPL at which each frequency in fArray is equal to i*10 phons, where i is the first dimension index
        
        """
    levels = range(20, 100, 20)
    elc = np.zeros((len(levels), len(fArray)))
    
    for i in range(len(levels)):
        if(verbose):
            print "Level: " + str(levels[i])
        elc[i] = get_elc(levels[i], xtol=xtol, verbose=verbose)
        plot_elc(elc[i], show=False)

    plt.show()

    return elc

def get_elc(SPL = 90, xtol=0.0000005, verbose=False):
    """
        Plot the prediction of the equal loudness contours for specified level.
        
        Parameters:
        * SPL (type: float) - SPL of the reference, or the loudness level
        * xtol (type: float) - x tolerance of fsolve
        * verbose (type: boolean) - I tell you what I know if you give me True
        
        Returns:
        * elc (type: numpy array of floats) - SPL at which each frequency in fArray is equal to SPL phons
        """
    fs = 44100
    t = np.array(range(44100))
    elc = np.zeros((len(fArray)))
    env = get_cos2_env(44100, fs)
    
    refSig = np.sin(1000 * t * 2 * np.pi / fs) * env
    refSig = refSig/rms(refSig)
    refLoudness = pp.calc_loudness_quiet(refSig, fs, SPL, rectify=False, inc_loud_region=True)[2].max()
    
    for i in range(len(fArray)):
        if (fArray[i] == 1000):
            elc[i] = SPL
        else:
            if(verbose):
                print fArray[i]
            input = np.sin(fArray[i] * t * 2 * np.pi / fs) * env
            input = input / rms(input)
            elc[i] = get_el(input, fs, refLoudness, SPL, xtol=xtol, verbose=verbose)

    return elc

def get_temporal_descrimination(verbose=False):
    """
        Return the prediction of STL and peak of STL for tones with short duration.
        
        Parameters
        * verbose (type: boolean) - I tell you what I know if you give me True
        
        Returns:
            * STLMax (type: array-like of floats) - peak short term loudness for each duration
            * STL (type: array-like of floats) - short term loudness function for each duration
        """


    fs = 44100
    STLMax = np.zeros((len(dArray)))
    STL = []
    
    for i in range(len(dArray)):
        if(verbose):
            print dArray[i]
        length = np.floor(dArray[i]*fs)
        env = get_cos2_env(length, fs, 0.005*fs)
        input = np.sin(np.arange(length)*2*np.pi*1000/fs) * env
        input = input/rms(input)
        STL.append(pp.calc_loudness_quiet(input, fs, 60, inc_loud_region=True)[2])
        STLMax[i] = STL[i].max()

    return STLMax, STL

def get_cos2_env(lengthSig, fs, lengthEnv = None):
    """
        Return a cos**2 envelope.
        
        Parameters:
            * lengthSig (type: int) - length of signal to be enveloped
            * fs - (type: int) - sampling frequency
            * lengthEnv (type: int) - length of ramps
            
        Returns:
            * env (type: numpy array of floats)- envelope
        """
    
    if(lengthEnv == None):
        lengthEnv = np.floor(0.02*fs)
    
    env = np.ones((lengthSig))
    T = int(lengthEnv)
    tRamp = np.array(range(T))
    f = 1/(lengthEnv/fs*4)
    env[tRamp] = np.sin(2*np.pi*f*tRamp/fs)**2
    env[-tRamp] = np.sin(2*np.pi*f*tRamp/fs)**2
    
    return env

def get_el(input, fs, refL, SPL = 90, xtol=0.0000005, verbose=False):
    """
        Get the SPL of the input at equal loudness to the reference.
        
        Parameters:
        * input (type: array-like of floats) - input signal
        * fs (type: int) - sampling frequency
        * SPL (type: float) - the level to start searching at
        * xtol (type: float) - x tolerance of fsolve
        * verbose (type: boolean) - I tell you what I know if you give me True
        
        Returns:
            x (type: float) - sound pressure level at equal loudness
        """
    
    x = opt.fsolve(getLoudnessDifference, SPL, (input, fs, refL, verbose), factor=0.1, xtol=xtol)
    
    return x

def getLoudnessDifference(SPL, input, fs, refL, verbose=False):
    """
        Used for get_el fsolve
        
        Parameters:
        * SPL (type: float) - the level to set input to
        * input (type: array-like of floats) - input signal
        * fs (type: int) - sampling frequency
        
        Returns:
            difference (type: float) - difference in loudness
        """
    if(verbose):
        print "SPL: ", SPL
    input = input/rms(input)
    compLoudness = pp.calc_loudness_quiet(input, fs, SPL, rectify=False, inc_loud_region=True)[2].max()
    difference = np.abs(refL - compLoudness)
    if(verbose):
        print "Difference: ", difference

    return difference

def rms(input):
    """
        RMS of the input
        
        Parameters:
        * input (type: array-like of floats): input array
        
        Returns:
        * output (type: float): the rms
        """
    output = np.sqrt(np.mean(input**2, -1))

    return output

def plot_elc(elc, show = True):
    """
        Plot an equal loudness contour
        
        Parameters:
            * elc (type: array-like of floats) - equal loudness contour x, obtained from get_elc
            * show (type: boolean) - show the figure or turn hold on
        """
    plt.gca().set_xscale('log')
    plt.ylim((0,170))
    plt.plot(fArray, elc, 'x-')
    if(show):
        plt.show()
    else:
        plt.hold(True)

    return