⛏️ index : haiku.git

/*
 * Copyright 2000-2006 Ingo Weinhold <ingo_weinhold@gmx.de>
 * All rights reserved. Distributed under the terms of the MIT licensce.
 */


#include "AudioResampler.h"

#include <stdio.h>

#include "SampleBuffer.h"


//#define TRACE_AUDIO_RESAMPLER
#ifdef TRACE_AUDIO_RESAMPLER
#	define TRACE(x...)	printf(x)
#else
#	define TRACE(x...)
#endif


//! Calculates the greatest common divider of /a/ and /b/.
template<typename T> inline
T
gcd(T a, T b)
{
	while (b != 0) {
		T r = a % b;
		a = b;
		b = r;
	}
	return a;
}


template<typename Buffer>
static void
resample_linear(void* _inBuffer, void* _outBuffer, uint32 channelCount,
	double inFrameRate, double outFrameRate, int32 frames)
{
	typedef double sample_t;
	Buffer inBuffer(_inBuffer);
	Buffer outFrameBuf(_outBuffer);
	for (sample_t outFrame = 0; outFrame < frames; outFrame++) {
		// time of the out sample
		sample_t outTime = outFrame / outFrameRate;
		// first in frame
		int64 inFrame = int64(outTime * inFrameRate);
		// time of the first and the second in frame
		sample_t inTime1 = (sample_t)inFrame / inFrameRate;
		sample_t inTime2 = (sample_t)(inFrame + 1) / inFrameRate;
		// differences between the out frame time and the in frame times
		sample_t timeDiff1 = outTime - inTime1;
		sample_t timeDiff2 = inTime2 - outTime;
		sample_t timeDiff = timeDiff1 + timeDiff2;
		// pointer to the first and second in frame
		Buffer inFrameBuf1 = inBuffer + inFrame * channelCount;
		Buffer inFrameBuf2 = inFrameBuf1 + channelCount;
		for (uint32 c = 0; c < channelCount;
			 c++, inFrameBuf1++, inFrameBuf2++, outFrameBuf++) {
			// sum weighted according to the distance to the respective other
			// in frame
			outFrameBuf.WriteSample((timeDiff2 * inFrameBuf1.ReadSample()
				+ timeDiff1 * inFrameBuf2.ReadSample()) / timeDiff);
		}
	}
}


// #pragma mark -


AudioResampler::AudioResampler()
	:
	AudioReader(),
	fSource(NULL),
	fTimeScale(1.0),
	fInOffset(0)
{
}


AudioResampler::AudioResampler(AudioReader* source, float frameRate,
		float timeScale)
	:
	AudioReader(),
	fSource(NULL),
	fTimeScale(timeScale),
	fInOffset(0)
{
	SetSource(source);
	if (fSource)
		fFormat.u.raw_audio.frame_rate = frameRate;
}


AudioResampler::~AudioResampler()
{
}


bigtime_t
AudioResampler::InitialLatency() const
{
	return fSource->InitialLatency();
}


status_t
AudioResampler::Read(void* buffer, int64 pos, int64 frames)
{
	TRACE("AudioResampler::Read(%p, %lld, %lld)\n", buffer, pos, frames);

	status_t error = InitCheck();
	if (error != B_OK) {
		TRACE("AudioResampler::Read() done1\n");
		return error;
	}
	// calculate position and frames in the source data
	int64 sourcePos = ConvertToSource(pos);
	int64 sourceFrames = ConvertToSource(pos + frames) - sourcePos;
	// check the frame counts
	if (sourceFrames == frames) {
		TRACE("AudioResampler::Read() done2\n");
		return fSource->Read(buffer, sourcePos, sourceFrames);
	}
	if (sourceFrames == 0) {
		ReadSilence(buffer, frames);
		TRACE("AudioResampler::Read() done3\n");
		return B_OK;
	}
	// check, if playing backwards
	bool backward = false;
	if (sourceFrames < 0) {
		sourceFrames = -sourceFrames;
		sourcePos -= sourceFrames;
		backward = true;
	}

	// we need at least two frames to interpolate
	sourceFrames += 2;
	int32 sampleSize = media_raw_audio_format::B_AUDIO_SIZE_MASK;
	uint32 channelCount = fFormat.u.raw_audio.channel_count;
	char* inBuffer = new char[sourceFrames * channelCount * sampleSize];
	error = fSource->Read(inBuffer, sourcePos, sourceFrames);
	if (error != B_OK) {
		TRACE("AudioResampler::_ReadLinear() done4\n");
		return error;
	}
	double inFrameRate = fSource->Format().u.raw_audio.frame_rate;
	double outFrameRate = (double)fFormat.u.raw_audio.frame_rate
		/ (double)fTimeScale;
	// choose the sample buffer to be used
	switch (fFormat.u.raw_audio.format) {
		case media_raw_audio_format::B_AUDIO_FLOAT:
			resample_linear< FloatSampleBuffer<double> >(inBuffer, buffer,
				channelCount, inFrameRate, outFrameRate, (int32)frames);
			break;
		case media_raw_audio_format::B_AUDIO_INT:
			resample_linear< IntSampleBuffer<double> >(inBuffer, buffer,
				channelCount, inFrameRate, outFrameRate, (int32)frames);
			break;
		case media_raw_audio_format::B_AUDIO_SHORT:
			resample_linear< ShortSampleBuffer<double> >(inBuffer, buffer,
				channelCount, inFrameRate, outFrameRate, (int32)frames);
			break;
		case media_raw_audio_format::B_AUDIO_UCHAR:
			resample_linear< UCharSampleBuffer<double> >(inBuffer, buffer,
				channelCount, inFrameRate, outFrameRate, (int32)frames);
			break;
		case media_raw_audio_format::B_AUDIO_CHAR:
			resample_linear< CharSampleBuffer<double> >(inBuffer, buffer,
				channelCount, inFrameRate, outFrameRate, (int32)frames);
			break;
	}
	// reverse the frame order if reading backwards
	if (backward)
		ReverseFrames(buffer, frames);
	delete[] inBuffer;
	TRACE("AudioResampler::Read() done\n");
	return B_OK;
}


status_t
AudioResampler::InitCheck() const
{
	status_t error = AudioReader::InitCheck();
	if (error == B_OK && !fSource)
		error = B_NO_INIT;
	return error;
}


void
AudioResampler::SetSource(AudioReader* source)
{
	if (!source) {
		TRACE("AudioResampler::SetSource() - NULL source\n");
		return;
	}

	if (source->Format().type != B_MEDIA_RAW_AUDIO) {
		TRACE("AudioResampler::SetSource() - not B_MEDIA_RAW_AUDIO\n");
		return;
	}

	uint32 hostByteOrder
		= (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
	if (source->Format().u.raw_audio.byte_order != hostByteOrder) {
		TRACE("AudioResampler::SetSource() - not host byte order\n");
		return;
	}

	float frameRate = FrameRate();
		// don't overwrite previous audio frame rate
	fSource = source;
	fFormat = source->Format();
	fFormat.u.raw_audio.frame_rate = frameRate;
}


void
AudioResampler::SetFrameRate(float frameRate)
{
	fFormat.u.raw_audio.frame_rate = frameRate;
}


void
AudioResampler::SetTimeScale(float timeScale)
{
	fTimeScale = timeScale;
}


AudioReader*
AudioResampler::Source() const
{
	return fSource;
}


float
AudioResampler::FrameRate() const
{
	return fFormat.u.raw_audio.frame_rate;
}


float
AudioResampler::TimeScale() const
{
	return fTimeScale;
}


void
AudioResampler::SetInOffset(int64 offset)
{
	fInOffset = offset;
}


int64
AudioResampler::InOffset() const
{
	return fInOffset;
}


int64
AudioResampler::ConvertFromSource(int64 pos) const
{
	double inFrameRate = fSource->Format().u.raw_audio.frame_rate;
	double outFrameRate = fFormat.u.raw_audio.frame_rate;
	return (int64)((double)(pos - fInOffset) * outFrameRate / inFrameRate
		/ (double)fTimeScale) - fOutOffset;
}


int64
AudioResampler::ConvertToSource(int64 pos) const
{
	double inFrameRate = fSource->Format().u.raw_audio.frame_rate;
	double outFrameRate = fFormat.u.raw_audio.frame_rate;
	return (int64)((double)(pos + fOutOffset) * inFrameRate / outFrameRate
		* (double)fTimeScale) + fInOffset;
}