1 /* FAudio - XAudio Reimplementation for FNA
2 *
3 * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
4 *
5 * This software is provided 'as-is', without any express or implied warranty.
6 * In no event will the authors be held liable for any damages arising from
7 * the use of this software.
8 *
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
12 *
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software in a
15 * product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
17 *
18 * 2. Altered source versions must be plainly marked as such, and must not be
19 * misrepresented as being the original software.
20 *
21 * 3. This notice may not be removed or altered from any source distribution.
22 *
23 * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
24 *
25 */
26
27 #include "FAudioFX.h"
28 #include "FAudio_internal.h"
29
30 /* Volume Meter FAPO Implementation */
31
32 const FAudioGUID FAudioFX_CLSID_AudioVolumeMeter = /* 2.7 */
33 {
34 0xCAC1105F,
35 0x619B,
36 0x4D04,
37 {
38 0x83,
39 0x1A,
40 0x44,
41 0xE1,
42 0xCB,
43 0xF1,
44 0x2D,
45 0x57
46 }
47 };
48
49 static FAPORegistrationProperties VolumeMeterProperties =
50 {
51 /* .clsid = */ {0},
52 /* .FriendlyName = */
53 {
54 'V', 'o', 'l', 'u', 'm', 'e', 'M', 'e', 't', 'e', 'r', '\0'
55 },
56 /*.CopyrightInfo = */
57 {
58 'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')',
59 'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0'
60 },
61 /*.MajorVersion = */ 0,
62 /*.MinorVersion = */ 0,
63 /*.Flags = */(
64 FAPO_FLAG_CHANNELS_MUST_MATCH |
65 FAPO_FLAG_FRAMERATE_MUST_MATCH |
66 FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH |
67 FAPO_FLAG_BUFFERCOUNT_MUST_MATCH |
68 FAPO_FLAG_INPLACE_SUPPORTED |
69 FAPO_FLAG_INPLACE_REQUIRED
70 ),
71 /*.MinInputBufferCount = */ 1,
72 /*.MaxInputBufferCount = */ 1,
73 /*.MinOutputBufferCount = */ 1,
74 /*.MaxOutputBufferCount =*/ 1
75 };
76
77 typedef struct FAudioFXVolumeMeter
78 {
79 FAPOBase base;
80 uint16_t channels;
81 } FAudioFXVolumeMeter;
82
FAudioFXVolumeMeter_LockForProcess(FAudioFXVolumeMeter * fapo,uint32_t InputLockedParameterCount,const FAPOLockForProcessBufferParameters * pInputLockedParameters,uint32_t OutputLockedParameterCount,const FAPOLockForProcessBufferParameters * pOutputLockedParameters)83 uint32_t FAudioFXVolumeMeter_LockForProcess(
84 FAudioFXVolumeMeter *fapo,
85 uint32_t InputLockedParameterCount,
86 const FAPOLockForProcessBufferParameters *pInputLockedParameters,
87 uint32_t OutputLockedParameterCount,
88 const FAPOLockForProcessBufferParameters *pOutputLockedParameters
89 ) {
90 FAudioFXVolumeMeterLevels *levels = (FAudioFXVolumeMeterLevels*)
91 fapo->base.m_pParameterBlocks;
92
93 /* Verify parameter counts... */
94 if ( InputLockedParameterCount < fapo->base.m_pRegistrationProperties->MinInputBufferCount ||
95 InputLockedParameterCount > fapo->base.m_pRegistrationProperties->MaxInputBufferCount ||
96 OutputLockedParameterCount < fapo->base.m_pRegistrationProperties->MinOutputBufferCount ||
97 OutputLockedParameterCount > fapo->base.m_pRegistrationProperties->MaxOutputBufferCount )
98 {
99 return FAUDIO_E_INVALID_ARG;
100 }
101
102
103 /* Validate input/output formats */
104 #define VERIFY_FORMAT_FLAG(flag, prop) \
105 if ( (fapo->base.m_pRegistrationProperties->Flags & flag) && \
106 (pInputLockedParameters->pFormat->prop != pOutputLockedParameters->pFormat->prop) ) \
107 { \
108 return FAUDIO_E_INVALID_ARG; \
109 }
110 VERIFY_FORMAT_FLAG(FAPO_FLAG_CHANNELS_MUST_MATCH, nChannels)
111 VERIFY_FORMAT_FLAG(FAPO_FLAG_FRAMERATE_MUST_MATCH, nSamplesPerSec)
112 VERIFY_FORMAT_FLAG(FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH, wBitsPerSample)
113 #undef VERIFY_FORMAT_FLAG
114 if ( (fapo->base.m_pRegistrationProperties->Flags & FAPO_FLAG_BUFFERCOUNT_MUST_MATCH) &&
115 (InputLockedParameterCount != OutputLockedParameterCount) )
116 {
117 return FAUDIO_E_INVALID_ARG;
118 }
119
120 /* Allocate volume meter arrays */
121 fapo->channels = pInputLockedParameters->pFormat->nChannels;
122 levels[0].pPeakLevels = (float*) fapo->base.pMalloc(
123 fapo->channels * sizeof(float) * 6
124 );
125 FAudio_zero(levels[0].pPeakLevels, fapo->channels * sizeof(float) * 6);
126 levels[0].pRMSLevels = levels[0].pPeakLevels + fapo->channels;
127 levels[1].pPeakLevels = levels[0].pPeakLevels + (fapo->channels * 2);
128 levels[1].pRMSLevels = levels[0].pPeakLevels + (fapo->channels * 3);
129 levels[2].pPeakLevels = levels[0].pPeakLevels + (fapo->channels * 4);
130 levels[2].pRMSLevels = levels[0].pPeakLevels + (fapo->channels * 5);
131
132 fapo->base.m_fIsLocked = 1;
133 return 0;
134 }
135
FAudioFXVolumeMeter_UnlockForProcess(FAudioFXVolumeMeter * fapo)136 void FAudioFXVolumeMeter_UnlockForProcess(FAudioFXVolumeMeter *fapo)
137 {
138 FAudioFXVolumeMeterLevels *levels = (FAudioFXVolumeMeterLevels*)
139 fapo->base.m_pParameterBlocks;
140 fapo->base.pFree(levels[0].pPeakLevels);
141 fapo->base.m_fIsLocked = 0;
142 }
143
FAudioFXVolumeMeter_Process(FAudioFXVolumeMeter * fapo,uint32_t InputProcessParameterCount,const FAPOProcessBufferParameters * pInputProcessParameters,uint32_t OutputProcessParameterCount,FAPOProcessBufferParameters * pOutputProcessParameters,int32_t IsEnabled)144 void FAudioFXVolumeMeter_Process(
145 FAudioFXVolumeMeter *fapo,
146 uint32_t InputProcessParameterCount,
147 const FAPOProcessBufferParameters* pInputProcessParameters,
148 uint32_t OutputProcessParameterCount,
149 FAPOProcessBufferParameters* pOutputProcessParameters,
150 int32_t IsEnabled
151 ) {
152 float peak;
153 float total;
154 float *buffer;
155 uint32_t i, j;
156 FAudioFXVolumeMeterLevels *levels = (FAudioFXVolumeMeterLevels*)
157 FAPOBase_BeginProcess(&fapo->base);
158
159 /* TODO: This could probably be SIMD-ified... */
160 for (i = 0; i < fapo->channels; i += 1)
161 {
162 peak = 0.0f;
163 total = 0.0f;
164 buffer = ((float*) pInputProcessParameters->pBuffer) + i;
165 for (j = 0; j < pInputProcessParameters->ValidFrameCount; j += 1, buffer += fapo->channels)
166 {
167 const float sampleAbs = FAudio_fabsf(*buffer);
168 if (sampleAbs > peak)
169 {
170 peak = sampleAbs;
171 }
172 total += (*buffer) * (*buffer);
173 }
174 levels->pPeakLevels[i] = peak;
175 levels->pRMSLevels[i] = FAudio_sqrtf(
176 total / pInputProcessParameters->ValidFrameCount
177 );
178 }
179
180 FAPOBase_EndProcess(&fapo->base);
181 }
182
FAudioFXVolumeMeter_GetParameters(FAudioFXVolumeMeter * fapo,FAudioFXVolumeMeterLevels * pParameters,uint32_t ParameterByteSize)183 void FAudioFXVolumeMeter_GetParameters(
184 FAudioFXVolumeMeter *fapo,
185 FAudioFXVolumeMeterLevels *pParameters,
186 uint32_t ParameterByteSize
187 ) {
188 FAudioFXVolumeMeterLevels *levels = (FAudioFXVolumeMeterLevels*)
189 fapo->base.m_pCurrentParameters;
190 FAudio_assert(ParameterByteSize == sizeof(FAudioFXVolumeMeterLevels));
191 FAudio_assert(pParameters->ChannelCount == fapo->channels);
192
193 /* Copy what's current as of the last Process */
194 if (pParameters->pPeakLevels != NULL)
195 {
196 FAudio_memcpy(
197 pParameters->pPeakLevels,
198 levels->pPeakLevels,
199 fapo->channels * sizeof(float)
200 );
201 }
202 if (pParameters->pRMSLevels != NULL)
203 {
204 FAudio_memcpy(
205 pParameters->pRMSLevels,
206 levels->pRMSLevels,
207 fapo->channels * sizeof(float)
208 );
209 }
210 }
211
FAudioFXVolumeMeter_Free(void * fapo)212 void FAudioFXVolumeMeter_Free(void* fapo)
213 {
214 FAudioFXVolumeMeter *volumemeter = (FAudioFXVolumeMeter*) fapo;
215 volumemeter->base.pFree(volumemeter->base.m_pParameterBlocks);
216 volumemeter->base.pFree(fapo);
217 }
218
219 /* Public API */
220
FAudioCreateVolumeMeter(FAPO ** ppApo,uint32_t Flags)221 uint32_t FAudioCreateVolumeMeter(FAPO** ppApo, uint32_t Flags)
222 {
223 return FAudioCreateVolumeMeterWithCustomAllocatorEXT(
224 ppApo,
225 Flags,
226 FAudio_malloc,
227 FAudio_free,
228 FAudio_realloc
229 );
230 }
231
FAudioCreateVolumeMeterWithCustomAllocatorEXT(FAPO ** ppApo,uint32_t Flags,FAudioMallocFunc customMalloc,FAudioFreeFunc customFree,FAudioReallocFunc customRealloc)232 uint32_t FAudioCreateVolumeMeterWithCustomAllocatorEXT(
233 FAPO** ppApo,
234 uint32_t Flags,
235 FAudioMallocFunc customMalloc,
236 FAudioFreeFunc customFree,
237 FAudioReallocFunc customRealloc
238 ) {
239 /* Allocate... */
240 FAudioFXVolumeMeter *result = (FAudioFXVolumeMeter*) customMalloc(
241 sizeof(FAudioFXVolumeMeter)
242 );
243 uint8_t *params = (uint8_t*) customMalloc(
244 sizeof(FAudioFXVolumeMeterLevels) * 3
245 );
246 FAudio_zero(params, sizeof(FAudioFXVolumeMeterLevels) * 3);
247
248 /* Initialize... */
249 FAudio_memcpy(
250 &VolumeMeterProperties.clsid,
251 &FAudioFX_CLSID_AudioVolumeMeter,
252 sizeof(FAudioGUID)
253 );
254 CreateFAPOBaseWithCustomAllocatorEXT(
255 &result->base,
256 &VolumeMeterProperties,
257 params,
258 sizeof(FAudioFXVolumeMeterLevels),
259 1,
260 customMalloc,
261 customFree,
262 customRealloc
263 );
264
265 /* Function table... */
266 result->base.base.LockForProcess = (LockForProcessFunc)
267 FAudioFXVolumeMeter_LockForProcess;
268 result->base.base.UnlockForProcess = (UnlockForProcessFunc)
269 FAudioFXVolumeMeter_UnlockForProcess;
270 result->base.base.Process = (ProcessFunc)
271 FAudioFXVolumeMeter_Process;
272 result->base.base.GetParameters = (GetParametersFunc)
273 FAudioFXVolumeMeter_GetParameters;
274 result->base.Destructor = FAudioFXVolumeMeter_Free;
275
276 /* Finally. */
277 *ppApo = &result->base.base;
278 return 0;
279 }
280
281 /* vim: set noexpandtab shiftwidth=8 tabstop=8: */
282