Time-stretching code

So here’s my time-stretching code for SDL_Mixer. Maybe this is the
easiest way to explain what I was trying to do :slight_smile:

Warning : the following code sucks. It also works. Feel free to
criticize and improve.

Mix_Chunk* Mix_TimeStretchChunk (Mix_Chunk* pChunk, float fFactor)
{
// Get the mixer settings
int nFreq, nChannels;
Uint16 nFormat;

Mix_QuerySpec(&nFreq, &nFormat, &nChannels);
	
int nBitsPerSample = nFormat & 0x1F;
int nBytesPerSample = nBitsPerSample/8;
bool bSigned = (nFormat & 0x8000) != 0;
bool bLSB = (nFormat & 0x1000) != 0;

GFC_VERIFY((nBitsPerSample % 8) == 0);

// TODO : support other formats
GFC_VERIFY(nBytesPerSample == 2);
GFC_VERIFY(!bLSB);


// Calculate original time, samples and bytes
int nOrigBytes = pChunk->alen;
int nOrigSamples = pChunk->alen / (nBytesPerSample*nChannels);
float fOrigSec = (float)nOrigSamples / (float)nFreq;	


// Calculate new time, samples and bytes
float fNewSec = fOrigSec*fFactor;
int nNewSamples = (int)(fNewSec*nFreq);
int nNewBytes = nNewSamples*nBytesPerSample*nChannels;


// Time-stretch
// Take fragments of size RESAMPLE_WINDOW_MS and paste them together,
// cross-fading the overlapping parts
static const int RESAMPLE_WINDOW_MS = 20;
static const int FADE_WINDOW_MS = 5;
	
short* pDstData = (short*)malloc(nChannels*nNewSamples*sizeof(short));
short* pSrcData = (short*)pChunk->abuf;

int nWindowSamples = (RESAMPLE_WINDOW_MS*nFreq)/1000;
int nFadeSamples = (FADE_WINDOW_MS*nFreq)/1000;
	
int nShiftSamples = nWindowSamples - nFadeSamples;
int nMidSamples = nWindowSamples - 2*nFadeSamples;

for (int nDstIdx = 0; nDstIdx < nNewSamples; nDstIdx += nShiftSamples)
{
	int nSrcIdx = (int)((float)nDstIdx/fFactor);
	short* pSrc = &pSrcData[2*nSrcIdx];
	short* pDst = &pDstData[2*nDstIdx];

	// Make sure we don't go past the end of the buffer
	// TODO : find a less stupid way to do this
	int nFadeSamplesEnd = nFadeSamples;

	int nExtra1 = GFC_MAX(0, nSrcIdx + nWindowSamples - nOrigSamples);
	int nExtra2 = GFC_MAX(0, nDstIdx + nWindowSamples - nNewSamples);
	int nExtra = GFC_MAX(nExtra1, nExtra2);

	if (nExtra > 0)
	{
		nFadeSamplesEnd -= nExtra;
		if (nFadeSamplesEnd < 0)
		{
			nMidSamples += nFadeSamplesEnd;
			nFadeSamplesEnd = 0;
		}
		
		if (nMidSamples < 0)
		{
			nFadeSamples += nMidSamples;
			nMidSamples = 0;
		}
		
		if (nFadeSamples < 0)
			nFadeSamples = 0;
	}
		
	
	// Copy and fade
	
	// TODO : Don't fade in the first sample. 5 ms is barely noticeable

though
float fMul, fDelta;
fMul = 0;
fDelta = 1.0f / nFadeSamples;
for (int j = 0; j < nFadeSamples; j++)
{
*pDst++ += (short)((*pSrc++)*fMul);
*pDst++ += (short)((*pSrc++)*fMul);

		fMul += fDelta;
	}
	
	for (int j = 0; j < nMidSamples; j++)
	{
		*pDst++ = *pSrc++;
		*pDst++ = *pSrc++;
	}
	
	
	// TODO : Don't fade out the last sample. 5 ms is barely noticeable

though
fMul = 1.0;
fDelta = -1.0f / nFadeSamples;
for (int j = 0; j < nFadeSamplesEnd; j++)
{
*pDst++ = (short)((*pSrc++)*fMul);
*pDst++ = (short)((*pSrc++)*fMul);

		fMul += fDelta;
	}
}


// Wrap in a chunk
Mix_Chunk* pRet = Mix_QuickLoad_RAW((Uint8*)pDstData, nNewBytes);
if (!pRet)
	free(pDstData);

return pRet;

}________________________________________________________________________
Gabriel Gambetta
Mystery Studio - http://www.mysterystudio.com
Gabriel’s Stuff - http://www.mysterystudio.com/gabriel

Gabriel Gambetta… Did you get success on build a time strech in SDL_mixer… I need help to construct it to…