view sv/filter/TimeStretchFilter.cpp @ 107:c3ac34b2e45b

correct bugs after DAN review
author lbajardsilogic
date Thu, 13 Sep 2007 12:42:44 +0000
parents d94ee3e8dfe1
children 71e5f393b727
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */

/*	Sound Access	
		EASAIER client application.	
		Silogic 2007. Laure Bajard. 
	
	This program is free software; you can redistribute it and/or    
	modify it under the terms of the GNU General Public License as    
	published by the Free Software Foundation; either version 2 of the    
	License, or (at your option) any later version.  See the file    
	COPYING included with this distribution for more information.
*/

#include <math.h>
#include <iostream>

#include "TimeStretchFilter.h"

#include  "FFTReal.h"
#include  "DSP.h"

#include "system/System.h"

/*float *audioframe;
float *prev_audioframe;
float *window;
float *processedframe;
float *outbuffer;
float *holdbuffer3;
float *holdbuffer2;
float *holdbuffer1;

float *c_phase;		///CURRENT FRAME phases
float *p_phase;		///PREVIOUS FRAME phases
float *c_synthphase;
float *p_synthphase;
float *synthframe;*/

float *c_mags;		///CURRENT FRAME MAGNITUDES
float *p_mags;		///PREVIOUS FRAME MAGNITUDES

float *FFTframe;

float hopfactor = 1;

//need in DSP.cpp
float lastfactor;

int numpeaks;
float *peak_locations; 
int currentposition = 1024+1;//= hop+1;
//

TimeStretchFilter::TimeStretchFilter() : Filter(),
	m_bypass(false),
	m_transcheck(false),
	m_peakcheck(false),
	m_framesize(4096),
	m_interpfactor(1),
	m_transhold(0)
{
	m_hop = m_framesize/4;

	m_inputBuffer = (float *)calloc(((m_framesize-1)*2 + 1 + m_hop), sizeof(float));
	
	/**********malloc***********/
	FFTframe=(float *)calloc((m_framesize), sizeof(float));
			
	//This block specifically sets up the buffers required to do a 75% overlap scheme
	audioframe=(float *)calloc((m_framesize), sizeof(float));			//The current frame
	prev_audioframe=(float *)calloc((m_framesize), sizeof(float));
	window=(float *)calloc((m_framesize), sizeof(float));				//Window
	processedframe=(float *)calloc((m_framesize), sizeof(float));			//The current frame
	synthframe=(float *)calloc((m_framesize), sizeof(float));
	outbuffer=(float *)calloc((m_framesize/4), sizeof(float));			//The current output segment which is 1/4 framesize for 75% overlap		
	
	holdbuffer3=(float *)calloc((m_framesize*0.75), sizeof(float));	//The hold buffer for the previous frame segment
	holdbuffer2=(float *)calloc((m_framesize/2), sizeof(float));		//The fold buffer for the frame segment 2 frames ago
	holdbuffer1=(float *)calloc((m_framesize/4), sizeof(float));

	c_mags=(float *)calloc((m_framesize/2), sizeof(float));			//The magnitude and phase arrays
	p_mags=(float *)calloc((m_framesize/2), sizeof(float));
	c_phase=(float *)calloc((m_framesize/2), sizeof(float));
	p_phase=(float *)calloc((m_framesize/2), sizeof(float));
	c_synthphase=(float *)calloc((m_framesize/2), sizeof(float));
	p_synthphase=(float *)calloc((m_framesize/2), sizeof(float));

	peak_locations=(float *)calloc((m_framesize/2), sizeof(float));

	hanning(window, m_framesize);

	/***************************/
}

TimeStretchFilter::~TimeStretchFilter()
{
	/**********de-alloc***********/
	delete m_inputBuffer;
	delete FFTframe;
			
	delete audioframe;		
	delete prev_audioframe;
	delete window;			
	delete processedframe;	
	delete synthframe;
			
	delete holdbuffer3;
	delete holdbuffer2;
	delete holdbuffer1;
			
	delete c_mags;
	delete p_mags;
	delete c_phase;
	delete p_phase;
	delete c_synthphase;
	delete p_synthphase;

	delete peak_locations;

	delete outbuffer;	

	/***************************/
}

TimeStretchFilter::PropertyList TimeStretchFilter::getProperties() const
{
	PropertyList list;
    list.push_back("Time");
	list.push_back("Pitch");
	list.push_back("Bypass");
	list.push_back("Transdetect");
	list.push_back("Peaklock");
    return list;
}

QString TimeStretchFilter::getPropertyLabel(const PropertyName &name) const
{
    if (name == "Time") return tr("Time");
	if (name == "Pitch") return tr("Pitch");
	if (name == "Bypass") return tr("Bypass Processing");
	if (name == "Transdetect") return tr("Transient Detection");
	if (name == "Peaklock") return tr("Peak Locking");
    return "";
}

TimeStretchFilter::PropertyType TimeStretchFilter::getPropertyType(const PropertyName &name) const
{
	if (name == "Time") return RangeProperty;
	if (name == "Pitch") return RangeProperty;
	if (name == "Bypass") return ToggleProperty;
	if (name == "Transdetect") return ToggleProperty;
	if (name == "Peaklock") return ToggleProperty;
    return InvalidProperty;
}

int TimeStretchFilter::getPropertyRangeAndValue(const PropertyName &name,
                                    int *min, int *max, int *deflt) const
{
    //!!! factor this colour handling stuff out into a colour manager class
	int val = 0;

    if (name == "Time") {
		if (min) *min = -100;
		if (max) *max = 100;
		if (deflt) *deflt = 0;
	}

	if (name == "Pitch") {
		if (min) *min = -100;
		if (max) *max = 100;
		if (deflt) *deflt = 0;
	}

	if (name == "Bypass") {
        if (deflt) *deflt = 0;
		val = (m_bypass ? 1 : 0);
    }

	if (name == "Transdetect") {
        if (deflt) *deflt = 0;
		val = (m_transcheck ? 1 : 0);
    }

	if (name == "Peaklock") {
        if (deflt) *deflt = 0;
		val = (m_peakcheck ? 1 : 0);
    }

    return val;
}

QString TimeStretchFilter::getPropertyValueLabel(const PropertyName &name,
				    int value) const
{
    if (name == "Time") {
		if (value == -100) 
			return tr("Slow");
		if (value == 100)
			return tr("Fast");
	}
    return tr("<unknown>");
}

void TimeStretchFilter::setProperty(const PropertyName &name, int value)
{
    if (name == "Time") {
		float tmaxfactor=2;
		if (value > 0){
			hopfactor=1.0+((tmaxfactor-1)*(((float)value)/100));
		}
		if (value < 0){
			hopfactor=1.0/(1.0+((tmaxfactor-1)*(-((float)value)/100)));
		}
		if(value == 0){
			hopfactor=1;
		}
	} else if (name == "Pitch") {
		float pmaxfactor=2;
		if (value > 0){
			m_interpfactor=1.0+((pmaxfactor-1)*(((float)value)/100));
		}
		if (value < 0){
			m_interpfactor=1.0/(1.0+((pmaxfactor-1)*(-((float)value)/100)));
		}
		if(value == 0){
			m_interpfactor=1;
		}
	} else if (name == "Bypass"){
		m_bypass = (value > 0) ? true : false;
	} else if (name == "Transdetect"){
		m_transcheck = (value > 0) ? true : false;
	} else if (name == "Peaklock"){
		m_peakcheck = (value > 0) ? true : false;
	}

}

void TimeStretchFilter::putInput(float **input, size_t samples)
{	
	int dd;
	float sampdiff;
	float difratio;
	float interpsample;

	bool drum = 0;
	float drumthresh = 65;

	int currentposition = m_hop;

	if (samples < floor((m_framesize-1)*m_interpfactor + 1 + m_hop))
		return;

	int channel = getSourceChannelCount();

	for (int i=0; i<samples; i++){
		if (channel > 1)
			m_inputBuffer[i] = input[0][i];// + input[1][i]) /2;
		else
			m_inputBuffer[i] = input[0][i];
	}
	
	for (int i = 0; i<m_framesize; i++)
	{
			
		//This block was specifically written to do resampling interpolation for crude pitch shifting
		//if it's not being used the audioframe line after the else should be used which is also used in bypass mode
		//At
		
		if (m_bypass == false) {
			dd = floor(double(i*m_interpfactor));
			difratio = (double(i*m_interpfactor)) - floor(double(i*m_interpfactor));
			
			// this block loads a frame as normal
			sampdiff=m_inputBuffer[dd+currentposition+1]-m_inputBuffer[dd+currentposition];
			interpsample = (difratio*sampdiff)+m_inputBuffer[dd+currentposition];
			audioframe[i] = interpsample*window[i];

			sampdiff=m_inputBuffer[dd+currentposition+1-m_hop]-m_inputBuffer[dd+currentposition-m_hop];
			interpsample = (difratio*sampdiff)+m_inputBuffer[dd+currentposition-m_hop];
			prev_audioframe[i] = interpsample*window[i];
		}
		else {
			audioframe[i] = m_inputBuffer[i+currentposition+1]*window[i];
			processedframe[i] = audioframe[i]*window[i];
		}
	}
	
	FFTReal fft_object(m_framesize);
			
	if (m_bypass == false)
	{ 
		fft_object.do_fft(FFTframe,audioframe);
	
		cart2pol(FFTframe, c_mags, c_phase, m_framesize);

		//--------------------------------------------
	
		fft_object.do_fft(FFTframe,prev_audioframe);

		cart2pol(FFTframe, p_mags, p_phase, m_framesize);
		
		drum = transient_detect(c_mags, c_mags, p_mags, p_mags, drumthresh, m_framesize);
	
	
		if (m_transcheck)
		{
	
			if (drum && m_transhold==0){
				cur2last(c_phase, c_synthphase, p_synthphase, m_framesize);
				m_transhold=4;
			}
			else{
				if(m_peakcheck){
					rotatephases_peaklocked(c_phase, p_phase, c_synthphase, p_synthphase, m_framesize, m_interpfactor);
				}
				else{
					rotatephases(c_phase, p_phase, c_synthphase, p_synthphase, m_framesize, m_interpfactor);
				}
			}
		}
		else
		{
			if(m_peakcheck){
				rotatephases_peaklocked(c_phase, p_phase, c_synthphase, p_synthphase, m_framesize, m_interpfactor);
			}
			else{
				rotatephases(c_phase, p_phase, c_synthphase, p_synthphase, m_framesize, m_interpfactor);
			}
		}
	
		if(m_transhold != 0){
			m_transhold = m_transhold - 1;
		}
	
		drum = 0;
		
		pol2cart(FFTframe, c_mags, c_synthphase, m_framesize);
		
		fft_object.do_ifft(FFTframe, processedframe);
		fft_object.rescale(processedframe); //VIP######## I have edited this function to do rewindowing also######
	}

	for (int p = 0; p<(m_framesize); p++){
		processedframe[p]=processedframe[p]*window[p];
	}
	
	for (int j = 0; j<(m_framesize); j++)
	{		
		//This block deals with the buffers for a 75% overlap scheme		
		
		if (j < m_framesize/4){
			outbuffer[j]=(processedframe[j]+holdbuffer1[j]+holdbuffer2[j]+holdbuffer3[j])*0.5;
			holdbuffer1[j]=holdbuffer2[j+(m_framesize/4)];
		}

		if (j < m_framesize/2){
			holdbuffer2[j]=holdbuffer3[j+(m_framesize/4)];
		}

		if (j < m_framesize*0.75){
			holdbuffer3[j]=processedframe[j+(m_framesize/4)];
		}
	}
}

void TimeStretchFilter::getOutput(float **output, size_t samples)
{
	if (samples > m_framesize/4)
		return;

	int channel = getSourceChannelCount();

	for (int i=0; i<samples; i++){
		output[0][i] = outbuffer[i];
		if (channel > 1)
			output[1][i] = outbuffer[i];
	}
}

size_t TimeStretchFilter::getRequiredInputSamples(size_t outputSamplesNeeded)
{
	size_t need = floor((m_framesize-1)*m_interpfactor + 1 + m_hop);
	return need;
}

size_t TimeStretchFilter::getRequiredSkipSamples()
{
	size_t skip = 1024;

	if (m_bypass == false && m_transhold==0)
	{
			skip = floor(m_hop*hopfactor);
	}
	else
	{		
			skip = m_hop;
	}

	return skip;
}