view sv/filter/TimeStretchFilter.cpp @ 84:51a12971e10e

specify the number of channels of the source to the filters
author lbajardsilogic
date Fri, 22 Jun 2007 13:35:26 +0000
parents 8ebc85f6ce4e
children 87495ac7710a
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_hop = m_framesize/4;

	m_inputBuffer = (float *)calloc((m_framesize*2+1), 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 transhold = 0;

	if (samples < floor(m_framesize*m_interpfactor + 1))
		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+1]-m_inputBuffer[dd];
			interpsample = (difratio*sampdiff)+m_inputBuffer[dd];
			audioframe[i] = (interpsample)*window[i];
		}
		else {
			audioframe[i] = (m_inputBuffer[i+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 && transhold==0){
				cur2last(c_phase, c_synthphase, p_synthphase, m_framesize);
				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(transhold != 0){
			transhold=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)];
		}
	}
	
	for (int i = 0; i<(m_framesize); i++)
	{
		prev_audioframe[i] = audioframe[i];
	}
}

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)
{
	// max (m_framesize, outputSamplesNeeded*2)
	size_t need = max( floor(m_framesize*m_interpfactor + 1), outputSamplesNeeded*2);
	
	return need;
}