-
-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
257 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,243 @@ | ||
/* FAudio - XAudio Reimplementation for FNA | ||
* | ||
* Copyright (c) 2011-2025 Ethan Lee, Katelyn Gadd, and the FNA 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. | ||
* | ||
* Katelyn Gadd <[email protected]> | ||
* | ||
*/ | ||
|
||
#include "FAudioFX.h" | ||
#include "FAudio_internal.h" | ||
#include "SDL_atomic.h" | ||
|
||
/* Sample Collector FAPO Implementation */ | ||
|
||
const FAudioGUID FAudioFX_CLSID_Collector = /* 2.7 */ | ||
{ | ||
0xCAC1105F, | ||
0x619B, | ||
0x4D04, | ||
{ | ||
0x83, | ||
0x1A, | ||
0x44, | ||
0xE1, | ||
0xCB, | ||
0xF1, | ||
0x2D, | ||
0x58 | ||
} | ||
}; | ||
|
||
static FAPORegistrationProperties CollectorProperties = | ||
{ | ||
/* .clsid = */ {0}, | ||
/* .FriendlyName = */ | ||
{ | ||
'C', 'o', 'l', 'l', 'e', 'c', 't', 'o', 'r', '\0' | ||
}, | ||
/*.CopyrightInfo = */ | ||
{ | ||
'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')', | ||
'K', 'a', 't', 'e', 'l', 'y', 'n', ' ', 'G', 'a', 'd', 'd', '\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 FAudioFXCollector | ||
{ | ||
FAPOBase base; | ||
uint16_t channels; | ||
float* pBuffer; | ||
uint32_t bufferLength; | ||
void** pSampleCounter; | ||
} FAudioFXCollector; | ||
|
||
uint32_t FAudioFXCollector_LockForProcess( | ||
FAudioFXCollector*fapo, | ||
uint32_t InputLockedParameterCount, | ||
const FAPOLockForProcessBufferParameters *pInputLockedParameters, | ||
uint32_t OutputLockedParameterCount, | ||
const FAPOLockForProcessBufferParameters *pOutputLockedParameters | ||
) { | ||
/* 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; | ||
} | ||
|
||
fapo->channels = pInputLockedParameters->pFormat->nChannels; | ||
fapo->base.m_fIsLocked = 1; | ||
return 0; | ||
} | ||
|
||
void FAudioFXCollector_UnlockForProcess(FAudioFXCollector*fapo) | ||
{ | ||
fapo->base.m_fIsLocked = 0; | ||
} | ||
|
||
void FAudioFXCollector_Process( | ||
FAudioFXCollector* fapo, | ||
uint32_t InputProcessParameterCount, | ||
const FAPOProcessBufferParameters* pInputProcessParameters, | ||
uint32_t OutputProcessParameterCount, | ||
FAPOProcessBufferParameters* pOutputProcessParameters, | ||
int32_t IsEnabled | ||
) { | ||
uint32_t channels = fapo->channels, | ||
bufferLength = fapo->bufferLength, | ||
sampleCount = pInputProcessParameters->ValidFrameCount; | ||
// We're supposed to be the only one updating the value of the counter, but we're doing atomic writes so let's do atomic reads too. | ||
uint64_t sampleCounter = (uint64_t)SDL_AtomicGetPtr(fapo->pSampleCounter); | ||
// Wrap the initial write offset (we'll wrap it around when it hits the end too.) | ||
uint32_t writeOffset = sampleCounter % bufferLength; | ||
float* pBuffer = (float*)pInputProcessParameters->pBuffer; | ||
float* pOutput = fapo->pBuffer; | ||
float multiplier = 1.0f / channels; | ||
|
||
for (uint32_t i = 0; i < sampleCount; i++) { | ||
// Accumulate all channels and average them | ||
float accumulator = 0; | ||
for (uint32_t j = 0; j < channels; j++) | ||
accumulator += pBuffer[j]; | ||
accumulator *= multiplier; | ||
|
||
// Advance to next set of samples | ||
pBuffer += channels; | ||
|
||
// Write to output buffer | ||
pOutput[writeOffset++] = accumulator; | ||
// Wrap write offset | ||
if (writeOffset >= bufferLength) | ||
writeOffset = 0; | ||
} | ||
|
||
// Update the sample counter in one go. SDL's only atomic 64-bit write is AtomicSetPtr and it's pointer sized. | ||
// We're the only code allowed to update this counter so we don't need to AtomicAdd or CAS, just atomic write to prevent tearing. | ||
SDL_AtomicSetPtr(fapo->pSampleCounter, (void*)(sampleCounter + sampleCount)); | ||
|
||
FAPOBase_EndProcess(&fapo->base); | ||
} | ||
|
||
void FAudioFXCollector_Free(void* fapo) | ||
{ | ||
FAudioFXCollector *collector = (FAudioFXCollector*) fapo; | ||
collector->base.pFree(fapo); | ||
} | ||
|
||
/* Public API */ | ||
|
||
uint32_t FAudioCreateCollector(FAPO** ppApo, uint32_t Flags, float* pBuffer, uint32_t bufferLength, void** pSampleCounter) | ||
{ | ||
return FAudioCreateCollectorWithCustomAllocatorEXT( | ||
ppApo, | ||
Flags, | ||
pBuffer, | ||
bufferLength, | ||
pSampleCounter, | ||
FAudio_malloc, | ||
FAudio_free, | ||
FAudio_realloc | ||
); | ||
} | ||
|
||
FAUDIOAPI uint32_t FAudioCreateCollectorWithCustomAllocatorEXT( | ||
FAPO** ppApo, | ||
uint32_t Flags, | ||
float* pBuffer, | ||
uint32_t bufferLength, | ||
void** pSampleCounter, | ||
FAudioMallocFunc customMalloc, | ||
FAudioFreeFunc customFree, | ||
FAudioReallocFunc customRealloc | ||
) { | ||
/* Allocate... */ | ||
FAudioFXCollector *result = (FAudioFXCollector*) customMalloc( | ||
sizeof(FAudioFXCollector) | ||
); | ||
|
||
/* Initialize... */ | ||
FAudio_memcpy( | ||
&CollectorProperties.clsid, | ||
&FAudioFX_CLSID_Collector, | ||
sizeof(FAudioGUID) | ||
); | ||
CreateFAPOBaseWithCustomAllocatorEXT( | ||
&result->base, | ||
&CollectorProperties, | ||
NULL, | ||
0, | ||
1, | ||
customMalloc, | ||
customFree, | ||
customRealloc | ||
); | ||
|
||
/* Function table... */ | ||
result->base.base.LockForProcess = (LockForProcessFunc) | ||
FAudioFXCollector_LockForProcess; | ||
result->base.base.UnlockForProcess = (UnlockForProcessFunc) | ||
FAudioFXCollector_UnlockForProcess; | ||
result->base.base.Process = (ProcessFunc) | ||
FAudioFXCollector_Process; | ||
result->base.base.GetParameters = NULL; | ||
result->base.Destructor = FAudioFXCollector_Free; | ||
|
||
/* Finally. */ | ||
*ppApo = &result->base.base; | ||
return 0; | ||
} | ||
|
||
/* vim: set noexpandtab shiftwidth=8 tabstop=8: */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters