102
* Audio Sniffer PDM device.
109
/* Internal audio input context, which makes sure that:
110
* - the filter audio input callback is not called after the filter has issued filter_input_end;
111
* - maintains internal information and state of the audio stream.
113
typedef struct SnifferInputCtx
115
/* Whether the context is still in use by the filter or I'll check. */
116
int32_t volatile cRefs;
118
/* The filter callback for incoming audio data. */
119
PFNAUDIOINPUTCALLBACK pfnFilterCallback;
120
void *pvFilterCallback;
122
/* Whether the stream has been ended by the filter. */
125
/* Context pointer returned by pfnAudioInputBegin. */
128
/* Audio format used for recording. */
131
/* Number of bytes per frame (bitsPerSample * channels) of the actual input format. */
132
uint32_t cBytesPerFrame;
134
/* Frequency of the actual audio format. */
137
/* Convertion from the actual input format to st_sample_t. */
140
/* If the actual format frequence differs from the requested format, this is not NULL. */
143
/* Temporary buffer for st_sample_t representation of the input audio data. */
144
void *pvSamplesBuffer;
145
uint32_t cbSamplesBufferAllocated;
147
/* Temporary buffer for frequency conversion. */
149
uint32_t cbRateBufferAllocated;
153
static void ictxDelete(SnifferInputCtx *pCtx)
155
/* The caller will not use this context anymore. */
158
st_rate_stop (pCtx->rate);
161
RTMemFree(pCtx->pvSamplesBuffer);
162
RTMemFree(pCtx->pvRateBuffer);
164
memset(pCtx, 0, sizeof(*pCtx));
168
static void ictxReallocSamplesBuffer(SnifferInputCtx *pCtx, uint32_t cs)
170
uint32_t cbBuffer = cs * sizeof(st_sample_t);
172
if (cbBuffer > pCtx->cbSamplesBufferAllocated)
174
RTMemFree(pCtx->pvSamplesBuffer);
176
pCtx->pvSamplesBuffer = RTMemAlloc(cbBuffer);
177
if (pCtx->pvSamplesBuffer)
179
pCtx->cbSamplesBufferAllocated = cbBuffer;
183
pCtx->cbSamplesBufferAllocated = 0;
188
static void ictxReallocRateBuffer(SnifferInputCtx *pCtx, uint32_t cs)
190
uint32_t cbBuffer = cs * sizeof(st_sample_t);
192
if (cbBuffer > pCtx->cbRateBufferAllocated)
194
RTMemFree(pCtx->pvRateBuffer);
196
pCtx->pvRateBuffer = RTMemAlloc(cbBuffer);
197
if (pCtx->pvRateBuffer)
199
pCtx->cbRateBufferAllocated = cbBuffer;
203
pCtx->cbRateBufferAllocated = 0;
210
* Filter audio output.
213
/* Whether the filter should intercept audio output. */
214
int filter_output_intercepted(void)
216
return 0; /* @todo Not implemented yet.*/
219
/* Filter informs that an audio output is starting. */
220
int filter_output_begin(void **ppvOutputCtx, struct audio_pcm_info *pinfo, int samples)
222
return VERR_NOT_SUPPORTED; /* @todo Not implemented yet.*/
225
/* Filter informs that the audio output has been stopped. */
226
void filter_output_end(void *pvOutputCtx)
228
return; /* @todo Not implemented yet.*/
232
* Filter audio input.
235
/* Whether the filter should intercept audio input. */
236
int filter_input_intercepted(void)
238
if (!g_pData || !g_pData->pDrv)
243
return g_pData->fInterceptAudioInput;
246
/* Filter informs that an audio input is starting. */
247
int filter_input_begin (void **ppvInputCtx, PFNAUDIOINPUTCALLBACK pfnCallback, void *pvCallback, HWVoiceIn *phw, int cSamples)
249
int rc = VINF_SUCCESS;
251
SnifferInputCtx *pCtx = NULL;
253
if (!g_pData || !g_pData->pDrv)
255
return VERR_NOT_SUPPORTED;
258
pCtx = (SnifferInputCtx *)RTMemAlloc(sizeof(SnifferInputCtx));
262
return VERR_NO_MEMORY;
265
pCtx->cRefs = 2; /* Context is used by both the filter and the user. */
266
pCtx->pfnFilterCallback = pfnCallback;
267
pCtx->pvFilterCallback = pvCallback;
268
pCtx->fEndedByFilter = false;
269
pCtx->pvUserCtx = NULL;
271
pCtx->cBytesPerFrame = 1;
275
pCtx->pvSamplesBuffer = NULL;
276
pCtx->cbSamplesBufferAllocated = 0;
277
pCtx->pvRateBuffer = NULL;
278
pCtx->cbRateBufferAllocated = 0;
280
rc = g_pData->pDrv->pfnAudioInputBegin (g_pData->pDrv,
281
&pCtx->pvUserCtx, /* Returned by the pDrv. */
283
cSamples, /* How many samples in one block is preferred. */
284
phw->info.freq, /* Required frequency. */
285
phw->info.nchannels, /* Number of audio channels. */
286
phw->info.bits); /* A sample size in one channel, samples are signed. */
297
Log(("input_begin rc = %Rrc\n", rc));
302
/* Filter informs that the audio input must be stopped. */
303
void filter_input_end(void *pvCtx)
307
SnifferInputCtx *pCtx = (SnifferInputCtx *)pvCtx;
309
void *pvUserCtx = pCtx->pvUserCtx;
311
pCtx->fEndedByFilter = true;
313
c = ASMAtomicDecS32(&pCtx->cRefs);
321
if (!g_pData || !g_pData->pDrv)
327
g_pData->pDrv->pfnAudioInputEnd (g_pData->pDrv,
330
Log(("input_end\n"));
337
static DECLCALLBACK(int) iface_AudioInputIntercept (PPDMIAUDIOSNIFFERPORT pInterface, bool fIntercept)
339
AUDIOSNIFFERSTATE *pThis = RT_FROM_MEMBER(pInterface, AUDIOSNIFFERSTATE, IPort);
341
Assert(g_pData == pThis);
343
pThis->fInterceptAudioInput = fIntercept;
348
static DECLCALLBACK(int) iface_AudioInputEventBegin (PPDMIAUDIOSNIFFERPORT pInterface,
357
AUDIOSNIFFERSTATE *pThis = RT_FROM_MEMBER(pInterface, AUDIOSNIFFERSTATE, IPort);
359
int rc = VINF_SUCCESS;
361
SnifferInputCtx *pCtx = (SnifferInputCtx *)pvContext;
363
Log(("FilterAudio: AudioInputEventBegin: %dHz,%dch,%dbits,%d ended %d\n",
364
iSampleHz, cChannels, cBits, fUnsigned, pCtx->fEndedByFilter));
366
Assert(g_pData == pThis);
368
/* Prepare a format convertion for the actually used format. */
369
pCtx->cBytesPerFrame = ((cBits + 7) / 8) * cChannels;
375
else if (cBits == 32)
384
pCtx->conv = mixeng_conv[(cChannels == 2)? 1: 0] /* stereo */
385
[!fUnsigned] /* sign */
389
if (iSampleHz && iSampleHz != pCtx->phw->info.freq)
391
pCtx->rate = st_rate_start (iSampleHz, pCtx->phw->info.freq);
392
pCtx->iFreq = iSampleHz;
398
static DECLCALLBACK(int) iface_AudioInputEventData (PPDMIAUDIOSNIFFERPORT pInterface,
403
AUDIOSNIFFERSTATE *pThis = RT_FROM_MEMBER(pInterface, AUDIOSNIFFERSTATE, IPort);
405
int rc = VINF_SUCCESS;
407
SnifferInputCtx *pCtx = (SnifferInputCtx *)pvContext;
409
Log(("FilterAudio: AudioInputEventData: pvData %p. cbData %d, ended %d\n", pvData, cbData, pCtx->fEndedByFilter));
411
Assert(g_pData == pThis);
413
if ( !pCtx->fEndedByFilter
416
/* Convert PCM samples to st_sample_t.
417
* And then apply rate conversion if necessary.
420
/* Optimization: allocate 'ps' buffer once per context and reallocate if cbData changes.
421
* Usually size of packets is constant.
424
uint32_t cs = cbData / pCtx->cBytesPerFrame; /* How many samples. */
426
ictxReallocSamplesBuffer(pCtx, cs);
428
ps = (st_sample_t *)pCtx->pvSamplesBuffer;
431
void *pvSamples = NULL;
432
uint32_t cbSamples = 0;
434
Assert(pCtx->cbSamplesBufferAllocated >= cs * sizeof(st_sample_t));
436
pCtx->conv(ps, pvData, cs, &nominal_volume);
440
st_sample_t *psConverted;
441
uint32_t csConverted = (cs * pCtx->phw->info.freq) / pCtx->iFreq;
443
ictxReallocRateBuffer(pCtx, csConverted);
445
psConverted = (st_sample_t *)pCtx->pvRateBuffer;
449
int csDst = csConverted;
451
Assert(pCtx->cbRateBufferAllocated >= csConverted * sizeof(st_sample_t));
453
st_rate_flow (pCtx->rate,
457
pvSamples = psConverted;
458
cbSamples = csDst * sizeof(st_sample_t); /* Use csDst as it may be != csConverted */
468
cbSamples = cs * sizeof(st_sample_t);
473
rc = pCtx->pfnFilterCallback(pCtx->pvFilterCallback, cbSamples, pvSamples);
485
static DECLCALLBACK(void) iface_AudioInputEventEnd (PPDMIAUDIOSNIFFERPORT pInterface,
490
AUDIOSNIFFERSTATE *pThis = RT_FROM_MEMBER(pInterface, AUDIOSNIFFERSTATE, IPort);
492
SnifferInputCtx *pCtx = (SnifferInputCtx *)pvContext;
494
Log(("FilterAudio: AudioInputEventEnd: ended %d\n", pCtx->fEndedByFilter));
496
Assert(g_pData == pThis);
498
c = ASMAtomicDecS32(&pCtx->cRefs);
105
507
static DECLCALLBACK(int) iface_Setup (PPDMIAUDIOSNIFFERPORT pInterface, bool fEnable, bool fKeepHostAudio)