|
|
/* FAudio - XAudio Reimplementation for FNA
|
|
|
*
|
|
|
* Copyright (c) 2011-2020 Ethan Lee, Luigi Auriemma, and the MonoGame Team
|
|
|
*
|
|
|
* This software is provided 'as-is', without any express or implied warranty.
|
|
|
* In no event will the authors be held liable for any damages arising from
|
|
|
* the use of this software.
|
|
|
*
|
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
|
* including commercial applications, and to alter it and redistribute it
|
|
|
* freely, subject to the following restrictions:
|
|
|
*
|
|
|
* 1. The origin of this software must not be misrepresented; you must not
|
|
|
* claim that you wrote the original software. If you use this software in a
|
|
|
* product, an acknowledgment in the product documentation would be
|
|
|
* appreciated but is not required.
|
|
|
*
|
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
|
* misrepresented as being the original software.
|
|
|
*
|
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
|
*
|
|
|
* Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
|
|
|
*
|
|
|
*/
|
|
|
|
|
|
#include "FAudioFX.h"
|
|
|
#include "FAudio_internal.h"
|
|
|
|
|
|
/* Volume Meter FAPO Implementation */
|
|
|
|
|
|
const FAudioGUID FAudioFX_CLSID_AudioVolumeMeter = /* 2.7 */
|
|
|
{
|
|
|
0xCAC1105F,
|
|
|
0x619B,
|
|
|
0x4D04,
|
|
|
{
|
|
|
0x83,
|
|
|
0x1A,
|
|
|
0x44,
|
|
|
0xE1,
|
|
|
0xCB,
|
|
|
0xF1,
|
|
|
0x2D,
|
|
|
0x57
|
|
|
}
|
|
|
};
|
|
|
|
|
|
static FAPORegistrationProperties VolumeMeterProperties =
|
|
|
{
|
|
|
/* .clsid = */ {0},
|
|
|
/* .FriendlyName = */
|
|
|
{
|
|
|
'V', 'o', 'l', 'u', 'm', 'e', 'M', 'e', 't', 'e', 'r', '\0'
|
|
|
},
|
|
|
/*.CopyrightInfo = */
|
|
|
{
|
|
|
'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')',
|
|
|
'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0'
|
|
|
},
|
|
|
/*.MajorVersion = */ 0,
|
|
|
/*.MinorVersion = */ 0,
|
|
|
/*.Flags = */(
|
|
|
FAPO_FLAG_CHANNELS_MUST_MATCH |
|
|
|
FAPO_FLAG_FRAMERATE_MUST_MATCH |
|
|
|
FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH |
|
|
|
FAPO_FLAG_BUFFERCOUNT_MUST_MATCH |
|
|
|
FAPO_FLAG_INPLACE_SUPPORTED |
|
|
|
FAPO_FLAG_INPLACE_REQUIRED
|
|
|
),
|
|
|
/*.MinInputBufferCount = */ 1,
|
|
|
/*.MaxInputBufferCount = */ 1,
|
|
|
/*.MinOutputBufferCount = */ 1,
|
|
|
/*.MaxOutputBufferCount =*/ 1
|
|
|
};
|
|
|
|
|
|
typedef struct FAudioFXVolumeMeter
|
|
|
{
|
|
|
FAPOBase base;
|
|
|
uint16_t channels;
|
|
|
} FAudioFXVolumeMeter;
|
|
|
|
|
|
uint32_t FAudioFXVolumeMeter_LockForProcess(
|
|
|
FAudioFXVolumeMeter *fapo,
|
|
|
uint32_t InputLockedParameterCount,
|
|
|
const FAPOLockForProcessBufferParameters *pInputLockedParameters,
|
|
|
uint32_t OutputLockedParameterCount,
|
|
|
const FAPOLockForProcessBufferParameters *pOutputLockedParameters
|
|
|
) {
|
|
|
FAudioFXVolumeMeterLevels *levels = (FAudioFXVolumeMeterLevels*)
|
|
|
fapo->base.m_pParameterBlocks;
|
|
|
|
|
|
/* Verify parameter counts... */
|
|
|
if ( InputLockedParameterCount < fapo->base.m_pRegistrationProperties->MinInputBufferCount ||
|
|
|
InputLockedParameterCount > fapo->base.m_pRegistrationProperties->MaxInputBufferCount ||
|
|
|
OutputLockedParameterCount < fapo->base.m_pRegistrationProperties->MinOutputBufferCount ||
|
|
|
OutputLockedParameterCount > fapo->base.m_pRegistrationProperties->MaxOutputBufferCount )
|
|
|
{
|
|
|
return FAUDIO_E_INVALID_ARG;
|
|
|
}
|
|
|
|
|
|
|
|
|
/* Validate input/output formats */
|
|
|
#define VERIFY_FORMAT_FLAG(flag, prop) \
|
|
|
if ( (fapo->base.m_pRegistrationProperties->Flags & flag) && \
|
|
|
(pInputLockedParameters->pFormat->prop != pOutputLockedParameters->pFormat->prop) ) \
|
|
|
{ \
|
|
|
return FAUDIO_E_INVALID_ARG; \
|
|
|
}
|
|
|
VERIFY_FORMAT_FLAG(FAPO_FLAG_CHANNELS_MUST_MATCH, nChannels)
|
|
|
VERIFY_FORMAT_FLAG(FAPO_FLAG_FRAMERATE_MUST_MATCH, nSamplesPerSec)
|
|
|
VERIFY_FORMAT_FLAG(FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH, wBitsPerSample)
|
|
|
#undef VERIFY_FORMAT_FLAG
|
|
|
if ( (fapo->base.m_pRegistrationProperties->Flags & FAPO_FLAG_BUFFERCOUNT_MUST_MATCH) &&
|
|
|
(InputLockedParameterCount != OutputLockedParameterCount) )
|
|
|
{
|
|
|
return FAUDIO_E_INVALID_ARG;
|
|
|
}
|
|
|
|
|
|
/* Allocate volume meter arrays */
|
|
|
fapo->channels = pInputLockedParameters->pFormat->nChannels;
|
|
|
levels[0].pPeakLevels = (float*) fapo->base.pMalloc(
|
|
|
fapo->channels * sizeof(float) * 6
|
|
|
);
|
|
|
FAudio_zero(levels[0].pPeakLevels, fapo->channels * sizeof(float) * 6);
|
|
|
levels[0].pRMSLevels = levels[0].pPeakLevels + fapo->channels;
|
|
|
levels[1].pPeakLevels = levels[0].pPeakLevels + (fapo->channels * 2);
|
|
|
levels[1].pRMSLevels = levels[0].pPeakLevels + (fapo->channels * 3);
|
|
|
levels[2].pPeakLevels = levels[0].pPeakLevels + (fapo->channels * 4);
|
|
|
levels[2].pRMSLevels = levels[0].pPeakLevels + (fapo->channels * 5);
|
|
|
|
|
|
fapo->base.m_fIsLocked = 1;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
void FAudioFXVolumeMeter_UnlockForProcess(FAudioFXVolumeMeter *fapo)
|
|
|
{
|
|
|
FAudioFXVolumeMeterLevels *levels = (FAudioFXVolumeMeterLevels*)
|
|
|
fapo->base.m_pParameterBlocks;
|
|
|
fapo->base.pFree(levels[0].pPeakLevels);
|
|
|
fapo->base.m_fIsLocked = 0;
|
|
|
}
|
|
|
|
|
|
void FAudioFXVolumeMeter_Process(
|
|
|
FAudioFXVolumeMeter *fapo,
|
|
|
uint32_t InputProcessParameterCount,
|
|
|
const FAPOProcessBufferParameters* pInputProcessParameters,
|
|
|
uint32_t OutputProcessParameterCount,
|
|
|
FAPOProcessBufferParameters* pOutputProcessParameters,
|
|
|
int32_t IsEnabled
|
|
|
) {
|
|
|
float peak;
|
|
|
float total;
|
|
|
float *buffer;
|
|
|
uint32_t i, j;
|
|
|
FAudioFXVolumeMeterLevels *levels = (FAudioFXVolumeMeterLevels*)
|
|
|
FAPOBase_BeginProcess(&fapo->base);
|
|
|
|
|
|
/* TODO: This could probably be SIMD-ified... */
|
|
|
for (i = 0; i < fapo->channels; i += 1)
|
|
|
{
|
|
|
peak = 0.0f;
|
|
|
total = 0.0f;
|
|
|
buffer = ((float*) pInputProcessParameters->pBuffer) + i;
|
|
|
for (j = 0; j < pInputProcessParameters->ValidFrameCount; j += 1, buffer += fapo->channels)
|
|
|
{
|
|
|
const float sampleAbs = FAudio_fabsf(*buffer);
|
|
|
if (sampleAbs > peak)
|
|
|
{
|
|
|
peak = sampleAbs;
|
|
|
}
|
|
|
total += (*buffer) * (*buffer);
|
|
|
}
|
|
|
levels->pPeakLevels[i] = peak;
|
|
|
levels->pRMSLevels[i] = FAudio_sqrtf(
|
|
|
total / pInputProcessParameters->ValidFrameCount
|
|
|
);
|
|
|
}
|
|
|
|
|
|
FAPOBase_EndProcess(&fapo->base);
|
|
|
}
|
|
|
|
|
|
void FAudioFXVolumeMeter_GetParameters(
|
|
|
FAudioFXVolumeMeter *fapo,
|
|
|
FAudioFXVolumeMeterLevels *pParameters,
|
|
|
uint32_t ParameterByteSize
|
|
|
) {
|
|
|
FAudioFXVolumeMeterLevels *levels = (FAudioFXVolumeMeterLevels*)
|
|
|
fapo->base.m_pCurrentParameters;
|
|
|
FAudio_assert(ParameterByteSize == sizeof(FAudioFXVolumeMeterLevels));
|
|
|
FAudio_assert(pParameters->ChannelCount == fapo->channels);
|
|
|
|
|
|
/* Copy what's current as of the last Process */
|
|
|
if (pParameters->pPeakLevels != NULL)
|
|
|
{
|
|
|
FAudio_memcpy(
|
|
|
pParameters->pPeakLevels,
|
|
|
levels->pPeakLevels,
|
|
|
fapo->channels * sizeof(float)
|
|
|
);
|
|
|
}
|
|
|
if (pParameters->pRMSLevels != NULL)
|
|
|
{
|
|
|
FAudio_memcpy(
|
|
|
pParameters->pRMSLevels,
|
|
|
levels->pRMSLevels,
|
|
|
fapo->channels * sizeof(float)
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void FAudioFXVolumeMeter_Free(void* fapo)
|
|
|
{
|
|
|
FAudioFXVolumeMeter *volumemeter = (FAudioFXVolumeMeter*) fapo;
|
|
|
volumemeter->base.pFree(volumemeter->base.m_pParameterBlocks);
|
|
|
volumemeter->base.pFree(fapo);
|
|
|
}
|
|
|
|
|
|
/* Public API */
|
|
|
|
|
|
uint32_t FAudioCreateVolumeMeter(FAPO** ppApo, uint32_t Flags)
|
|
|
{
|
|
|
return FAudioCreateVolumeMeterWithCustomAllocatorEXT(
|
|
|
ppApo,
|
|
|
Flags,
|
|
|
FAudio_malloc,
|
|
|
FAudio_free,
|
|
|
FAudio_realloc
|
|
|
);
|
|
|
}
|
|
|
|
|
|
uint32_t FAudioCreateVolumeMeterWithCustomAllocatorEXT(
|
|
|
FAPO** ppApo,
|
|
|
uint32_t Flags,
|
|
|
FAudioMallocFunc customMalloc,
|
|
|
FAudioFreeFunc customFree,
|
|
|
FAudioReallocFunc customRealloc
|
|
|
) {
|
|
|
/* Allocate... */
|
|
|
FAudioFXVolumeMeter *result = (FAudioFXVolumeMeter*) customMalloc(
|
|
|
sizeof(FAudioFXVolumeMeter)
|
|
|
);
|
|
|
uint8_t *params = (uint8_t*) customMalloc(
|
|
|
sizeof(FAudioFXVolumeMeterLevels) * 3
|
|
|
);
|
|
|
FAudio_zero(params, sizeof(FAudioFXVolumeMeterLevels) * 3);
|
|
|
|
|
|
/* Initialize... */
|
|
|
FAudio_memcpy(
|
|
|
&VolumeMeterProperties.clsid,
|
|
|
&FAudioFX_CLSID_AudioVolumeMeter,
|
|
|
sizeof(FAudioGUID)
|
|
|
);
|
|
|
CreateFAPOBaseWithCustomAllocatorEXT(
|
|
|
&result->base,
|
|
|
&VolumeMeterProperties,
|
|
|
params,
|
|
|
sizeof(FAudioFXVolumeMeterLevels),
|
|
|
1,
|
|
|
customMalloc,
|
|
|
customFree,
|
|
|
customRealloc
|
|
|
);
|
|
|
|
|
|
/* Function table... */
|
|
|
result->base.base.LockForProcess = (LockForProcessFunc)
|
|
|
FAudioFXVolumeMeter_LockForProcess;
|
|
|
result->base.base.UnlockForProcess = (UnlockForProcessFunc)
|
|
|
FAudioFXVolumeMeter_UnlockForProcess;
|
|
|
result->base.base.Process = (ProcessFunc)
|
|
|
FAudioFXVolumeMeter_Process;
|
|
|
result->base.base.GetParameters = (GetParametersFunc)
|
|
|
FAudioFXVolumeMeter_GetParameters;
|
|
|
result->base.Destructor = FAudioFXVolumeMeter_Free;
|
|
|
|
|
|
/* Finally. */
|
|
|
*ppApo = &result->base.base;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
|
|
|
|