// Copyright (C) 2020 Daniel Mall
#include "CAudioMixMultiSource.h"
#include <cassert>

void CAudioMixMultiSource::Deallocate()
{
	if (m_final == nullptr)
	{
		return;
	}

	free(m_final);

	for (float* pBuffer : m_eachSamples)
	{
		free(pBuffer);
	}
}

//! allocate the number of audio frames for the audio mixer
void CAudioMixMultiSource::AllocateWav16(const int nAudioFrames)
{
	assert(m_nAllocatedSizeInFrames == 0);
	if (m_nAllocatedSizeInFrames == 0)
	{
		m_nAllocatedSizeInFrames = nAudioFrames;
		m_final = (float*)malloc(sizeof(float) * m_nAllocatedSizeInFrames * WAV16_CHANNELS);

		for (int i = 0; i < (AUDIOMIXER_MAXSOUNDS + 1); i++)
		{
			const int nAllocatedSamples = m_nAllocatedSizeInFrames * WAV16_CHANNELS;
			float* pBuffer = (float*)malloc(sizeof(float) * nAllocatedSamples);
			for (int k = 0; k < nAllocatedSamples; k++)
			{
				pBuffer[k] = 0.0f;
			}
			m_eachSamples.push_back(pBuffer);
			m_eachFrames.push_back(0);
		}
	}
}

//! reset the audio mixer for the next audio request
void CAudioMixMultiSource::Reset()
{
	m_nIndexSource = 0;
	m_nIndexSample = 0;
	m_nFinalFrames = 0;
}

//! move to the next audio source
void CAudioMixMultiSource::NextSource()
{
	assert(m_nIndexSource <= AUDIOMIXER_MAXSOUNDS);
	if (m_nIndexSource <= AUDIOMIXER_MAXSOUNDS)
	{
		m_nIndexSource++;
		m_nIndexSample = 0;
	}
}

//! add audio smaples to the mixer
void CAudioMixMultiSource::AddSamplesWav16(const int16_t* pBuffer, const int nAudioFrames)
{
	// total frames verification
	const int nFramesAlready = (m_nIndexSample / 2);
	const int nFramesTotal = (nFramesAlready + nAudioFrames);
	assert(nFramesTotal <= m_nAllocatedSizeInFrames);
	if (nFramesTotal > m_nFinalFrames)
	{
		m_nFinalFrames = nFramesTotal;
	}
	m_eachFrames[m_nIndexSource] = nFramesTotal;

	float* pSamples = m_eachSamples[m_nIndexSource];

	const int nAudioSamples = nAudioFrames * WAV16_CHANNELS;
	for (int i = 0; i < nAudioSamples; i++)
	{
		const int16_t nSample = pBuffer[i];
		float fSample = nSample;
		if (nSample > 0)
		{
			fSample = fSample / 32767;
		}
		else if (nSample < 0)
		{
			fSample = fSample / 32768;
		}

		pSamples[m_nIndexSample] = fSample;
		m_nIndexSample++;
	}
}

//! audio mixing algorithm
void CAudioMixMultiSource::MixSourcesWav16()
{
	const int nFinalSamples = m_nFinalFrames * WAV16_CHANNELS;
	for (int nSample = 0; nSample <nFinalSamples; nSample++)
	{
		int nTotalSources = 0;
		float fSample = 0.0f;
		for (int nSource = 0; nSource < m_nIndexSource; nSource++)
		{
			const int nAvailableSamplesInSource = (m_eachFrames[nSource] * WAV16_CHANNELS);
			if (nSample <= nAvailableSamplesInSource)
			{
				float* pSamples = m_eachSamples[nSource];
				fSample += pSamples[nSample];
				nTotalSources++;
			}
		}

		// apply some clipping
		if (fSample > 1.0f)
		{
			fSample = 1.0f;
		}
		else if (fSample < -1.0f)
		{
			fSample = -1.0f;
		}

		m_final[nSample] = fSample;
	}
}
