VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/filteraudio.c@ 40754

Last change on this file since 40754 was 39104, checked in by vboxsync, 13 years ago

Audio: warnings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.2 KB
Line 
1/* $Id: filteraudio.c 39104 2011-10-25 06:36:29Z vboxsync $ */
2/** @file
3 * VBox audio devices: filter driver, which sits between the host audio driver
4 * and the virtual audio device and intercept all host driver operations.
5 *
6 * The filter is used mostly for remote audio input.
7 */
8
9/*
10 * Copyright (C) 2010 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 */
20
21#define LOG_GROUP LOG_GROUP_DEV_AUDIO
22#include <VBox/log.h>
23#include <iprt/asm.h>
24#include <iprt/mem.h>
25#include <iprt/cdefs.h>
26
27#define AUDIO_CAP "filteraudio"
28#include "vl_vbox.h"
29#include "audio.h"
30#include "audio_int.h"
31
32#define FILTER_EXTENSIVE_LOGGING
33
34/*******************************************************************************
35 *
36 * IO Ring Buffer section
37 *
38 ******************************************************************************/
39
40/* Implementation of a lock free ring buffer which could be used in a multi
41 * threaded environment. Note that only the acquire, release and getter
42 * functions are threading aware. So don't use reset if the ring buffer is
43 * still in use. */
44typedef struct IORINGBUFFER
45{
46 /* The current read position in the buffer */
47 uint32_t uReadPos;
48 /* The current write position in the buffer */
49 uint32_t uWritePos;
50 /* How much space of the buffer is currently in use */
51 volatile uint32_t cBufferUsed;
52 /* How big is the buffer */
53 uint32_t cBufSize;
54 /* The buffer itself */
55 char *pBuffer;
56} IORINGBUFFER;
57/* Pointer to an ring buffer structure */
58typedef IORINGBUFFER* PIORINGBUFFER;
59
60
61static void IORingBufferCreate(PIORINGBUFFER *ppBuffer, uint32_t cbSize)
62{
63 PIORINGBUFFER pTmpBuffer;
64
65 AssertPtr(ppBuffer);
66
67 *ppBuffer = NULL;
68 pTmpBuffer = RTMemAllocZ(sizeof(IORINGBUFFER));
69 if (pTmpBuffer)
70 {
71 pTmpBuffer->pBuffer = RTMemAlloc(cbSize);
72 if(pTmpBuffer->pBuffer)
73 {
74 pTmpBuffer->cBufSize = cbSize;
75 *ppBuffer = pTmpBuffer;
76 }
77 else
78 RTMemFree(pTmpBuffer);
79 }
80}
81
82static void IORingBufferDestroy(PIORINGBUFFER pBuffer)
83{
84 if (pBuffer)
85 {
86 if (pBuffer->pBuffer)
87 RTMemFree(pBuffer->pBuffer);
88 RTMemFree(pBuffer);
89 }
90}
91
92DECL_FORCE_INLINE(void) IORingBufferReset(PIORINGBUFFER pBuffer)
93{
94 AssertPtr(pBuffer);
95
96 pBuffer->uReadPos = 0;
97 pBuffer->uWritePos = 0;
98 pBuffer->cBufferUsed = 0;
99}
100
101DECL_FORCE_INLINE(uint32_t) IORingBufferFree(PIORINGBUFFER pBuffer)
102{
103 AssertPtr(pBuffer);
104 return pBuffer->cBufSize - ASMAtomicReadU32(&pBuffer->cBufferUsed);
105}
106
107DECL_FORCE_INLINE(uint32_t) IORingBufferUsed(PIORINGBUFFER pBuffer)
108{
109 AssertPtr(pBuffer);
110 return ASMAtomicReadU32(&pBuffer->cBufferUsed);
111}
112
113DECL_FORCE_INLINE(uint32_t) IORingBufferSize(PIORINGBUFFER pBuffer)
114{
115 AssertPtr(pBuffer);
116 return pBuffer->cBufSize;
117}
118
119static void IORingBufferAquireReadBlock(PIORINGBUFFER pBuffer, uint32_t cReqSize, char **ppStart, uint32_t *pcSize)
120{
121 uint32_t uUsed = 0;
122 uint32_t uSize = 0;
123
124 AssertPtr(pBuffer);
125
126 *ppStart = 0;
127 *pcSize = 0;
128
129 /* How much is in use? */
130 uUsed = ASMAtomicReadU32(&pBuffer->cBufferUsed);
131 if (uUsed > 0)
132 {
133 /* Get the size out of the requested size, the read block till the end
134 * of the buffer & the currently used size. */
135 uSize = RT_MIN(cReqSize, RT_MIN(pBuffer->cBufSize - pBuffer->uReadPos, uUsed));
136 if (uSize > 0)
137 {
138 /* Return the pointer address which point to the current read
139 * position. */
140 *ppStart = pBuffer->pBuffer + pBuffer->uReadPos;
141 *pcSize = uSize;
142 }
143 }
144}
145
146DECL_FORCE_INLINE(void) IORingBufferReleaseReadBlock(PIORINGBUFFER pBuffer, uint32_t cSize)
147{
148 AssertPtr(pBuffer);
149
150 /* Split at the end of the buffer. */
151 pBuffer->uReadPos = (pBuffer->uReadPos + cSize) % pBuffer->cBufSize;
152
153 ASMAtomicSubU32(&pBuffer->cBufferUsed, cSize);
154}
155
156static void IORingBufferAquireWriteBlock(PIORINGBUFFER pBuffer, uint32_t cReqSize, char **ppStart, uint32_t *pcSize)
157{
158 uint32_t uFree;
159 uint32_t uSize;
160
161 AssertPtr(pBuffer);
162
163 *ppStart = 0;
164 *pcSize = 0;
165
166 /* How much is free? */
167 uFree = pBuffer->cBufSize - ASMAtomicReadU32(&pBuffer->cBufferUsed);
168 if (uFree > 0)
169 {
170 /* Get the size out of the requested size, the write block till the end
171 * of the buffer & the currently free size. */
172 uSize = RT_MIN(cReqSize, RT_MIN(pBuffer->cBufSize - pBuffer->uWritePos, uFree));
173 if (uSize > 0)
174 {
175 /* Return the pointer address which point to the current write
176 * position. */
177 *ppStart = pBuffer->pBuffer + pBuffer->uWritePos;
178 *pcSize = uSize;
179 }
180 }
181}
182
183DECL_FORCE_INLINE(void) IORingBufferReleaseWriteBlock(PIORINGBUFFER pBuffer, uint32_t cSize)
184{
185 AssertPtr(pBuffer);
186
187 /* Split at the end of the buffer. */
188 pBuffer->uWritePos = (pBuffer->uWritePos + cSize) % pBuffer->cBufSize;
189
190 ASMAtomicAddU32(&pBuffer->cBufferUsed, cSize);
191}
192
193/*******************************************************************************
194 *
195 * Global structures section
196 *
197 ******************************************************************************/
198
199/* Initialization status indicator used for the recreation of the AudioUnits. */
200#define CA_STATUS_UNINIT UINT32_C(0) /* The device is uninitialized */
201#define CA_STATUS_IN_INIT UINT32_C(1) /* The device is currently initializing */
202#define CA_STATUS_INIT UINT32_C(2) /* The device is initialized */
203#define CA_STATUS_IN_UNINIT UINT32_C(3) /* The device is currently uninitializing */
204
205struct
206{
207 struct audio_driver *pDrv;
208 void *pDrvOpaque;
209} filter_conf =
210{
211 INIT_FIELD(.pDrv =) NULL,
212 INIT_FIELD(.pDrvOpaque =) NULL
213};
214
215/*
216 * filterVoiceOut and filterVoiceIn are allocated at the end of the original driver HWVoice structure:
217 * {
218 * HWVoiceOut;
219 * OriginalDriverHWVoiceData;
220 * filterVoiceOut;
221 * }
222 */
223typedef struct filterVoiceOut
224{
225 /* HW voice input structure, which prepends the filterVoiceOut. */
226 HWVoiceOut *phw;
227
228 /* A ring buffer for transferring data to the playback thread */
229 PIORINGBUFFER pBuf;
230
231 /* Initialization status tracker. Used when some of the device parameters
232 * or the device itself is changed during the runtime. */
233 volatile uint32_t status;
234
235 /* Whether the output stream is used by the filter. */
236 bool fIntercepted;
237
238 /* Whether this stream is active. */
239 bool fIsRunning;
240
241 /* Sniffer level context for this audio output stream. */
242 void *pvOutputCtx;
243} filterVoiceOut;
244
245typedef struct filterVoiceIn
246{
247 /* HW voice input structure, which prepends the filterVoiceIn. */
248 HWVoiceIn *phw;
249
250 /* A temporary position value. */
251 uint32_t rpos;
252
253 /* A ring buffer for transferring data from the recording thread */
254 PIORINGBUFFER pBuf;
255
256 /* Initialization status tracker. Used when some of the device parameters
257 * or the device itself is changed during the runtime. */
258 volatile uint32_t status;
259
260 /* the stream has been successfully initialized by host. */
261 bool fHostOK;
262
263 /* Whether the input stream is used by the filter. */
264 bool fIntercepted;
265
266 /* Whether this stream is active. */
267 bool fIsRunning;
268
269 /* Sniffer level context for this audio input stream. */
270 void *pvInputCtx;
271} filterVoiceIn;
272
273#ifdef FILTER_EXTENSIVE_LOGGING
274# define CA_EXT_DEBUG_LOG(a) Log(a)
275#else
276# define CA_EXT_DEBUG_LOG(a) do {} while(0)
277#endif
278
279/*******************************************************************************
280 *
281 * CoreAudio output section
282 *
283 ******************************************************************************/
284
285/* We need some forward declarations */
286static int filteraudio_run_out(HWVoiceOut *hw);
287static int filteraudio_write(SWVoiceOut *sw, void *buf, int len);
288static int filteraudio_ctl_out(HWVoiceOut *hw, int cmd, ...);
289static void filteraudio_fini_out(HWVoiceOut *hw);
290static int filteraudio_init_out(HWVoiceOut *hw, audsettings_t *as);
291static int caInitOutput(HWVoiceOut *hw);
292static void caReinitOutput(HWVoiceOut *hw);
293
294static int fltInitOutput(filterVoiceOut *pVoice)
295{
296 uint32_t cFrames; /* default frame count */
297 uint32_t cSamples; /* samples count */
298
299 ASMAtomicXchgU32(&pVoice->status, CA_STATUS_IN_INIT);
300
301 cFrames = 2048;
302
303 /* Create the internal ring buffer. */
304 cSamples = cFrames * pVoice->phw->info.nchannels;
305 IORingBufferCreate(&pVoice->pBuf, cSamples << pVoice->phw->info.shift);
306 if (!RT_VALID_PTR(pVoice->pBuf))
307 {
308 LogRel(("FilterAudio: [Output] Failed to create internal ring buffer\n"));
309 return -1;
310 }
311
312 if ( pVoice->phw->samples != 0
313 && pVoice->phw->samples != (int32_t)cSamples)
314 LogRel(("FilterAudio: [Output] Warning! After recreation, the CoreAudio ring buffer doesn't has the same size as the device buffer (%RU32 vs. %RU32).\n", cSamples, (uint32_t)pVoice->phw->samples));
315 ASMAtomicXchgU32(&pVoice->status, CA_STATUS_INIT);
316
317 Log(("FilterAudio: [Output] Frame count: %RU32\n", cFrames));
318
319 return 0;
320}
321
322static int filteraudio_run_out(HWVoiceOut *phw)
323{
324 uint32_t csAvail = 0;
325 uint32_t cbToWrite = 0;
326 uint32_t csToWrite = 0;
327 uint32_t csWritten = 0;
328 char *pcDst = NULL;
329 st_sample_t *psSrc = NULL;
330
331 filterVoiceOut *pVoice = (filterVoiceOut *)((uint8_t *)phw + filter_conf.pDrv->voice_size_out);
332
333 if (!pVoice->fIntercepted)
334 {
335 return filter_conf.pDrv->pcm_ops->run_out(phw);
336 }
337
338 /* We return the live count in the case we are not initialized. This should
339 * prevent any under runs. */
340 if (ASMAtomicReadU32(&pVoice->status) != CA_STATUS_INIT)
341 return audio_pcm_hw_get_live_out(pVoice->phw);
342
343 /* Make sure the device is running */
344 filteraudio_ctl_out(pVoice->phw, VOICE_ENABLE);
345
346 /* How much space is available in the ring buffer */
347 csAvail = IORingBufferFree(pVoice->pBuf) >> pVoice->phw->info.shift; /* bytes -> samples */
348
349 /* How much data is available. Use the smaller size of the too. */
350 csAvail = RT_MIN(csAvail, (uint32_t)audio_pcm_hw_get_live_out(pVoice->phw));
351
352 CA_EXT_DEBUG_LOG(("FilterAudio: [Output] Start writing buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << pVoice->phw->info.shift));
353
354 /* Iterate as long as data is available */
355 while (csWritten < csAvail)
356 {
357 /* How much is left? Split request at the end of our samples buffer. */
358 csToWrite = RT_MIN(csAvail - csWritten, (uint32_t)(pVoice->phw->samples - pVoice->phw->rpos));
359 cbToWrite = csToWrite << pVoice->phw->info.shift; /* samples -> bytes */
360 CA_EXT_DEBUG_LOG(("FilterAudio: [Output] Try writing %RU32 samples (%RU32 bytes)\n", csToWrite, cbToWrite));
361
362 /* Try to acquire the necessary space from the ring buffer. */
363 IORingBufferAquireWriteBlock(pVoice->pBuf, cbToWrite, &pcDst, &cbToWrite);
364
365 /* How much to we get? */
366 csToWrite = cbToWrite >> pVoice->phw->info.shift;
367 CA_EXT_DEBUG_LOG(("FilterAudio: [Output] There is space for %RU32 samples (%RU32 bytes) available\n", csToWrite, cbToWrite));
368
369 /* Break if nothing is free anymore. */
370 if (RT_UNLIKELY(cbToWrite == 0))
371 break;
372
373 /* Copy the data from our mix buffer to the ring buffer. */
374 psSrc = pVoice->phw->mix_buf + pVoice->phw->rpos;
375 pVoice->phw->clip((uint8_t*)pcDst, psSrc, csToWrite);
376
377 /* Release the ring buffer, so the read thread could start reading this data. */
378 IORingBufferReleaseWriteBlock(pVoice->pBuf, cbToWrite);
379
380 pVoice->phw->rpos = (pVoice->phw->rpos + csToWrite) % pVoice->phw->samples;
381
382 /* How much have we written so far. */
383 csWritten += csToWrite;
384 }
385
386 CA_EXT_DEBUG_LOG(("FilterAudio: [Output] Finished writing buffer with %RU32 samples (%RU32 bytes)\n", csWritten, csWritten << pVoice->phw->info.shift));
387
388 /* Return the count of samples we have processed. */
389 return csWritten;
390}
391
392static int filteraudio_write(SWVoiceOut *sw, void *buf, int len)
393{
394 /* Every host backend just calls the generic function, so no need to forward. */
395 return audio_pcm_sw_write (sw, buf, len);
396}
397
398static int filteraudio_ctl_out(HWVoiceOut *phw, int cmd, ...)
399{
400 uint32_t status;
401
402 filterVoiceOut *pVoice = (filterVoiceOut *)((uint8_t *)phw + filter_conf.pDrv->voice_size_out);
403
404 if (!pVoice->fIntercepted)
405 {
406 /* Note: audio.c does not use variable parameters '...', so ok to forward only 'phw' and 'cmd'. */
407 return filter_conf.pDrv->pcm_ops->ctl_out(phw, cmd);
408 }
409
410 status = ASMAtomicReadU32(&pVoice->status);
411 if (!(status == CA_STATUS_INIT))
412 return 0;
413
414 switch (cmd)
415 {
416 case VOICE_ENABLE:
417 {
418 /* Only start the device if it is actually stopped */
419 if (!pVoice->fIsRunning)
420 {
421 IORingBufferReset(pVoice->pBuf);
422 filter_output_begin(&pVoice->pvOutputCtx, &pVoice->phw->info, pVoice->phw->samples);
423 }
424 break;
425 }
426 case VOICE_DISABLE:
427 {
428 /* Only stop the device if it is actually running */
429 if (pVoice->fIsRunning)
430 {
431 filter_output_end(pVoice->pvOutputCtx);
432 }
433 break;
434 }
435 }
436 return 0;
437}
438
439static void filteraudio_fini_out(HWVoiceOut *phw)
440{
441 int rc = 0;
442 uint32_t status;
443
444 filterVoiceOut *pVoice = (filterVoiceOut *)((uint8_t *)phw + filter_conf.pDrv->voice_size_out);
445
446 if (!pVoice->fIntercepted)
447 {
448 filter_conf.pDrv->pcm_ops->fini_out(phw);
449 return;
450 }
451
452 status = ASMAtomicReadU32(&pVoice->status);
453 if (!(status == CA_STATUS_INIT))
454 return;
455
456 rc = filteraudio_ctl_out(phw, VOICE_DISABLE);
457 if (RT_LIKELY(rc == 0))
458 {
459 ASMAtomicXchgU32(&pVoice->status, CA_STATUS_IN_UNINIT);
460 IORingBufferDestroy(pVoice->pBuf);
461 pVoice->pBuf = NULL;
462 ASMAtomicXchgU32(&pVoice->status, CA_STATUS_UNINIT);
463 }
464 else
465 LogRel(("FilterAudio: [Output] Failed to stop playback (%RI32)\n", rc));
466}
467
468static int filteraudio_init_out(HWVoiceOut *phw, audsettings_t *as)
469{
470 int rc = 0;
471
472 filterVoiceOut *pVoice = (filterVoiceOut *)((uint8_t *)phw + filter_conf.pDrv->voice_size_out);
473
474 if (!filter_output_intercepted())
475 {
476 pVoice->fIntercepted = false;
477 return filter_conf.pDrv->pcm_ops->init_out(phw, as);
478 }
479
480 /* Output is not tested and is not used currently */
481 AssertFailed();
482 return -1;
483
484 ASMAtomicXchgU32(&pVoice->status, CA_STATUS_UNINIT);
485
486 pVoice->fIntercepted = true;
487 pVoice->phw = phw;
488 pVoice->phw->samples = 0;
489
490 /* Initialize the hardware info section with the audio settings */
491 audio_pcm_init_info(&pVoice->phw->info, as);
492
493 rc = fltInitOutput(pVoice);
494 if (RT_UNLIKELY(rc != 0))
495 return rc;
496
497 /* The samples have to correspond to the internal ring buffer size. */
498 pVoice->phw->samples = (IORingBufferSize(pVoice->pBuf) >> pVoice->phw->info.shift) / pVoice->phw->info.nchannels;
499
500 Log(("FilterAudio: [Output] HW samples: %d\n", pVoice->phw->samples));
501
502 return 0;
503}
504
505/*******************************************************************************
506 *
507 * FilterAudio input section
508 *
509 ******************************************************************************/
510
511/*
512 * Callback to feed audio input buffer. Samples format is be the same as
513 * in the voice. The caller prepares st_sample_t.
514 *
515 * @param cbSamples Size of pvSamples array in bytes.
516 * @param pvSamples Points to an array of samples.
517 *
518 * @return IPRT status code.
519 */
520static DECLCALLBACK(int) fltRecordingCallback(void* pvCallback,
521 uint32_t cbSamples,
522 const void *pvSamples)
523{
524 int rc = VINF_SUCCESS;
525 uint32_t csAvail = 0;
526 uint32_t csToWrite = 0;
527 uint32_t cbToWrite = 0;
528 uint32_t csWritten = 0;
529 char *pcDst = NULL;
530
531 filterVoiceIn *pVoice = (filterVoiceIn *)pvCallback;
532
533 Assert((cbSamples % sizeof(st_sample_t)) == 0);
534
535 if (!pVoice->fIsRunning)
536 return VINF_SUCCESS;
537
538 /* If nothing is pending return immediately. */
539 if (cbSamples == 0)
540 return VINF_SUCCESS;
541
542 /* How much space is free in the ring buffer? */
543 csAvail = IORingBufferFree(pVoice->pBuf) / sizeof(st_sample_t); /* bytes -> samples */
544
545 /* How much space is used in the audio buffer. Use the smaller size of the too. */
546 csAvail = RT_MIN(csAvail, cbSamples / sizeof(st_sample_t));
547
548 CA_EXT_DEBUG_LOG(("FilterAudio: [Input] Start writing buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail * sizeof(st_sample_t)));
549
550 /* Iterate as long as data is available */
551 while(csWritten < csAvail)
552 {
553 /* How much is left? */
554 csToWrite = csAvail - csWritten;
555 cbToWrite = csToWrite * sizeof(st_sample_t);
556 CA_EXT_DEBUG_LOG(("FilterAudio: [Input] Try writing %RU32 samples (%RU32 bytes)\n", csToWrite, cbToWrite));
557
558 /* Try to acquire the necessary space from the ring buffer. */
559 IORingBufferAquireWriteBlock(pVoice->pBuf, cbToWrite, &pcDst, &cbToWrite);
560
561 /* How much do we get? */
562 csToWrite = cbToWrite / sizeof(st_sample_t);
563 CA_EXT_DEBUG_LOG(("FilterAudio: [Input] There is space for %RU32 samples (%RU32 bytes) available\n", csToWrite, cbToWrite));
564
565 /* Break if nothing is free anymore. */
566 if (RT_UNLIKELY(csToWrite == 0))
567 break;
568
569 /* Copy the data from the audio buffer to the ring buffer. */
570 memcpy(pcDst, (uint8_t *)pvSamples + (csWritten * sizeof(st_sample_t)), cbToWrite);
571
572 /* Release the ring buffer, so the main thread could start reading this data. */
573 IORingBufferReleaseWriteBlock(pVoice->pBuf, cbToWrite);
574
575 csWritten += csToWrite;
576 }
577
578 CA_EXT_DEBUG_LOG(("FilterAudio: [Input] Finished writing buffer with %RU32 samples (%RU32 bytes)\n", csWritten, csWritten * sizeof(st_sample_t)));
579
580 return rc;
581}
582
583static int filteraudio_run_in(HWVoiceIn *phw)
584{
585 uint32_t csAvail = 0;
586 uint32_t cbToRead = 0;
587 uint32_t csToRead = 0;
588 uint32_t csReads = 0;
589 char *pcSrc;
590 st_sample_t *psDst;
591 filterVoiceIn *pVoice;
592
593 if (!filter_conf.pDrv)
594 {
595 AssertFailed();
596 return -1;
597 }
598
599 pVoice = (filterVoiceIn *)((uint8_t *)phw + filter_conf.pDrv->voice_size_in);
600
601 if (!pVoice->fIntercepted)
602 {
603 if (!pVoice->fHostOK)
604 {
605 /* Host did not initialize the voice. */
606 Log(("FilterAudio: [Input]: run_in voice %p (hw %p) not available on host\n", pVoice, pVoice->phw));
607 return -1;
608 }
609
610 Log(("FilterAudio: [Input]: forwarding run_in for voice %p (hw %p)\n", pVoice, pVoice->phw));
611 return filter_conf.pDrv->pcm_ops->run_in(phw);
612 }
613
614 Log(("FilterAudio: [Input]: run_in for voice %p (hw %p)\n", pVoice, pVoice->phw));
615
616 if (!pVoice->fIsRunning)
617 return 0;
618
619 /* How much space is used in the ring buffer? */
620 csAvail = IORingBufferUsed(pVoice->pBuf) / sizeof(st_sample_t); /* bytes -> samples */
621
622 /* How much space is available in the mix buffer. Use the smaller size of the too. */
623 csAvail = RT_MIN(csAvail, (uint32_t)(pVoice->phw->samples - audio_pcm_hw_get_live_in (pVoice->phw)));
624 CA_EXT_DEBUG_LOG(("FilterAudio: [Input] Start reading buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail * sizeof(st_sample_t)));
625
626 /* Iterate as long as data is available */
627 while (csReads < csAvail)
628 {
629 /* How much is left? Split request at the end of our samples buffer. */
630 csToRead = RT_MIN(csAvail - csReads, (uint32_t)(pVoice->phw->samples - pVoice->phw->wpos));
631 cbToRead = csToRead * sizeof(st_sample_t);
632 CA_EXT_DEBUG_LOG(("FilterAudio: [Input] Try reading %RU32 samples (%RU32 bytes)\n", csToRead, cbToRead));
633
634 /* Try to acquire the necessary block from the ring buffer. */
635 IORingBufferAquireReadBlock(pVoice->pBuf, cbToRead, &pcSrc, &cbToRead);
636
637 /* How much to we get? */
638 csToRead = cbToRead / sizeof(st_sample_t);
639 CA_EXT_DEBUG_LOG(("FilterAudio: [Input] There are %RU32 samples (%RU32 bytes) available\n", csToRead, cbToRead));
640
641 /* Break if nothing is used anymore. */
642 if (csToRead == 0)
643 break;
644
645 /* Copy the data from our ring buffer to the mix buffer. */
646 psDst = pVoice->phw->conv_buf + pVoice->phw->wpos;
647 memcpy(psDst, pcSrc, cbToRead);
648
649 /* Release the read buffer, so it could be used for new data. */
650 IORingBufferReleaseReadBlock(pVoice->pBuf, cbToRead);
651
652 pVoice->phw->wpos = (pVoice->phw->wpos + csToRead) % pVoice->phw->samples;
653
654 /* How much have we reads so far. */
655 csReads += csToRead;
656 }
657
658 CA_EXT_DEBUG_LOG(("FilterAudio: [Input] Finished reading buffer with %RU32 samples (%RU32 bytes)\n", csReads, csReads * sizeof(st_sample_t)));
659
660 return csReads;
661}
662
663static int filteraudio_read(SWVoiceIn *sw, void *buf, int size)
664{
665 /* Every host backend just calls the generic function, so no need to forward. */
666 return audio_pcm_sw_read (sw, buf, size);
667}
668
669static int filteraudio_ctl_in(HWVoiceIn *phw, int cmd, ...)
670{
671 int rc = VINF_SUCCESS;
672 filterVoiceIn *pVoice;
673
674 if (!filter_conf.pDrv)
675 {
676 AssertFailed();
677 return -1;
678 }
679
680 pVoice = (filterVoiceIn *)((uint8_t *)phw + filter_conf.pDrv->voice_size_in);
681
682 if (cmd == VOICE_ENABLE)
683 {
684 /* Decide who will provide input audio: filter or host driver. */
685 if (!filter_input_intercepted())
686 {
687 if (!pVoice->fHostOK)
688 {
689 /* Host did not initialize the voice. */
690 Log(("FilterAudio: [Input]: ctl_in ENABLE voice %p (hw %p) not available on host\n", pVoice, pVoice->phw));
691 return -1;
692 }
693
694 /* Note: audio.c does not use variable parameters '...', so ok to forward only 'phw' and 'cmd'. */
695 Log(("FilterAudio: [Input]: forwarding ctl_in ENABLE for voice %p (hw %p)\n", pVoice, pVoice->phw));
696 return filter_conf.pDrv->pcm_ops->ctl_in(phw, cmd);
697 }
698
699 /* The filter will use this voice. */
700 Log(("FilterAudio: [Input]: ctl_in ENABLE for voice %p (hw %p), cmd %d\n", pVoice, pVoice->phw, cmd));
701
702 if (ASMAtomicReadU32(&pVoice->status) != CA_STATUS_INIT)
703 return -1;
704
705 /* Only start the device if it is actually stopped */
706 if (!pVoice->fIsRunning)
707 {
708 IORingBufferReset(pVoice->pBuf);
709
710 /* Sniffer will inform us on a second thread for new incoming audio data.
711 * Therefore register an callback function, which will process the new data.
712 * */
713 rc = filter_input_begin(&pVoice->pvInputCtx, fltRecordingCallback, pVoice, pVoice->phw, pVoice->phw->samples);
714 if (RT_SUCCESS(rc))
715 {
716 pVoice->fIsRunning = true;
717
718 /* Remember that this voice is used by the filter. */
719 pVoice->fIntercepted = true;
720 }
721 }
722 if (RT_FAILURE(rc))
723 {
724 LogRel(("FilterAudio: [Input] Failed to start recording (%Rrc)\n", rc));
725 return -1;
726 }
727 }
728 else if (cmd == VOICE_DISABLE)
729 {
730 if (ASMAtomicReadU32(&pVoice->status) != CA_STATUS_INIT)
731 return -1;
732
733 /* Check if the voice has been intercepted. */
734 if (!pVoice->fIntercepted)
735 {
736 if (!pVoice->fHostOK)
737 {
738 /* Host did not initialize the voice. Theoretically should not happen, because
739 * audio.c should not disable a voice which has not been enabled at all.
740 */
741 Log(("FilterAudio: [Input]: ctl_in DISABLE voice %p (hw %p) not available on host\n", pVoice, pVoice->phw));
742 return -1;
743 }
744
745 /* Note: audio.c does not use variable parameters '...', so ok to forward only 'phw' and 'cmd'. */
746 Log(("FilterAudio: [Input]: forwarding ctl_in DISABLE for voice %p (hw %p)\n", pVoice, pVoice->phw));
747 return filter_conf.pDrv->pcm_ops->ctl_in(phw, cmd);
748 }
749
750 /* The filter used this voice. */
751 Log(("FilterAudio: [Input]: ctl_in DISABLE for voice %p (hw %p), cmd %d\n", pVoice, pVoice->phw, cmd));
752
753 /* Only stop the device if it is actually running */
754 if (pVoice->fIsRunning)
755 {
756 pVoice->fIsRunning = false;
757 /* Tell the sniffer to not to use this context anymore. */
758 filter_input_end(pVoice->pvInputCtx);
759 }
760
761 /* This voice is no longer used by the filter. */
762 pVoice->fIntercepted = false;
763 }
764 else
765 {
766 return -1; /* Unknown command. */
767 }
768
769 return 0;
770}
771
772static void filteraudio_fini_in(HWVoiceIn *phw)
773{
774 int ret = -1;
775 filterVoiceIn *pVoice;
776
777 if (!filter_conf.pDrv)
778 {
779 AssertFailed();
780 return;
781 }
782
783 pVoice = (filterVoiceIn *)((uint8_t *)phw + filter_conf.pDrv->voice_size_in);
784
785 /* Uninitialize both host and filter parts of the voice. */
786 if (pVoice->fHostOK)
787 {
788 /* Uninit host part only if it was initialized by host. */
789 Log(("FilterAudio: [Input]: forwarding fini_in for voice %p (hw %p)\n", pVoice, pVoice->phw));
790 filter_conf.pDrv->pcm_ops->fini_in(phw);
791 }
792
793 Log(("FilterAudio: [Input]: fini_in for voice %p (hw %p)\n", pVoice, pVoice->phw));
794
795 if (ASMAtomicReadU32(&pVoice->status) != CA_STATUS_INIT)
796 return;
797
798 /* If this voice is intercepted by filter, try to stop it. */
799 if (pVoice->fIntercepted)
800 {
801 ret = filteraudio_ctl_in(phw, VOICE_DISABLE);
802 }
803 else
804 {
805 ret = 0;
806 }
807
808 if (RT_LIKELY(ret == 0))
809 {
810 ASMAtomicWriteU32(&pVoice->status, CA_STATUS_IN_UNINIT);
811 IORingBufferDestroy(pVoice->pBuf);
812 pVoice->pBuf = NULL;
813 pVoice->rpos = 0;
814 ASMAtomicWriteU32(&pVoice->status, CA_STATUS_UNINIT);
815 }
816 else
817 LogRel(("FilterAudio: [Input] Failed to stop recording (%RI32)\n", ret));
818}
819
820static int filteraudio_init_in(HWVoiceIn *phw, audsettings_t *as)
821{
822 int hostret = -1;
823 filterVoiceIn *pVoice;
824
825 if (!filter_conf.pDrv)
826 {
827 AssertFailed();
828 return -1;
829 }
830
831 pVoice = (filterVoiceIn *)((uint8_t *)phw + filter_conf.pDrv->voice_size_in);
832
833 /* Initialize both host and filter parts of the voice. */
834 Log(("FilterAudio: [Input]: forwarding init_in for voice %p (hw %p)\n", pVoice, pVoice->phw));
835 hostret = filter_conf.pDrv->pcm_ops->init_in(phw, as);
836
837 Log(("FilterAudio: [Input]: init_in for voice %p (hw %p), hostret = %d\n", pVoice, pVoice->phw, hostret));
838
839 ASMAtomicWriteU32(&pVoice->status, CA_STATUS_UNINIT);
840
841 pVoice->phw = phw;
842 pVoice->rpos = 0;
843 pVoice->pBuf = NULL;
844 pVoice->fHostOK = (hostret == 0);
845 pVoice->fIntercepted = false;
846 pVoice->fIsRunning = false;
847 pVoice->pvInputCtx = NULL;
848
849 if (!pVoice->fHostOK)
850 {
851 /* Initialize required fields of the common part of the voice. */
852 pVoice->phw->samples = 2048;
853
854 /* Initialize the hardware info section with the audio settings */
855 audio_pcm_init_info(&pVoice->phw->info, as);
856 }
857
858 ASMAtomicWriteU32(&pVoice->status, CA_STATUS_IN_INIT);
859
860 /* Create the internal ring buffer. */
861 IORingBufferCreate(&pVoice->pBuf, pVoice->phw->samples * sizeof(st_sample_t));
862
863 if (!RT_VALID_PTR(pVoice->pBuf))
864 {
865 LogRel(("FilterAudio: [Input] Failed to create internal ring buffer\n"));
866 return -1;
867 }
868
869 ASMAtomicWriteU32(&pVoice->status, CA_STATUS_INIT);
870
871 Log(("FilterAudio: [Input] HW samples: %d\n", pVoice->phw->samples));
872 return 0;
873}
874
875/*******************************************************************************
876 *
877 * FilterAudio global section
878 *
879 ******************************************************************************/
880
881static void *filteraudio_audio_init(void)
882{
883 /* This is not supposed to be called. */
884 Log(("FilterAudio: Init\n"));
885 AssertFailed();
886 return NULL;
887}
888
889static void filteraudio_audio_fini(void *opaque)
890{
891 Log(("FilterAudio: Init fini %p\n", opaque));
892 /* Forward to the host driver. */
893 Assert(opaque == filter_conf.pDrvOpaque);
894 if (filter_conf.pDrv)
895 {
896 filter_conf.pDrv->fini(opaque);
897 filter_conf.pDrv = NULL;
898 filter_conf.pDrvOpaque = NULL;
899 }
900}
901
902static struct audio_pcm_ops filteraudio_pcm_ops =
903{
904 filteraudio_init_out,
905 filteraudio_fini_out,
906 filteraudio_run_out,
907 filteraudio_write,
908 filteraudio_ctl_out,
909
910 filteraudio_init_in,
911 filteraudio_fini_in,
912 filteraudio_run_in,
913 filteraudio_read,
914 filteraudio_ctl_in
915};
916
917static struct audio_driver filteraudio_audio_driver =
918{
919 INIT_FIELD(name =) "filteraudio",
920 INIT_FIELD(descr =)
921 "FilterAudio: filter driver between host audio and virtual device",
922 INIT_FIELD(options =) NULL,
923 INIT_FIELD(init =) filteraudio_audio_init,
924 INIT_FIELD(fini =) filteraudio_audio_fini,
925 INIT_FIELD(pcm_ops =) &filteraudio_pcm_ops,
926 INIT_FIELD(can_be_default =) 1,
927 INIT_FIELD(max_voices_out =) 1,
928 INIT_FIELD(max_voices_in =) 1,
929 INIT_FIELD(voice_size_out =) sizeof(filterVoiceOut),
930 INIT_FIELD(voice_size_in =) sizeof(filterVoiceIn)
931};
932
933struct audio_driver *filteraudio_install(struct audio_driver *pDrv, void *pDrvOpaque)
934{
935 Log(("FilterAudio: [Install]: intercepting driver [%s]\n", pDrv->name));
936
937 /* Modify the audio driver structure to be like the original driver. */
938 filteraudio_audio_driver.name = pDrv->name;
939 filteraudio_audio_driver.descr = pDrv->descr;
940 filteraudio_audio_driver.options = pDrv->options;
941 filteraudio_audio_driver.can_be_default = pDrv->can_be_default;
942 filteraudio_audio_driver.max_voices_out = pDrv->max_voices_out;
943 filteraudio_audio_driver.max_voices_in = pDrv->max_voices_in;
944 filteraudio_audio_driver.voice_size_out = pDrv->voice_size_out + sizeof(filterVoiceOut);
945 filteraudio_audio_driver.voice_size_in = pDrv->voice_size_in + sizeof(filterVoiceIn);
946
947 filter_conf.pDrv = pDrv;
948 filter_conf.pDrvOpaque = pDrvOpaque;
949
950 return &filteraudio_audio_driver;
951}
952
953int filteraudio_is_host_voice_in_ok(struct audio_driver *pDrv, HWVoiceIn *phw)
954{
955 filterVoiceIn *pVoice;
956
957 if (pDrv != &filteraudio_audio_driver)
958 {
959 /* This is not the driver for which the filter was installed.
960 * The filter has no idea and assumes that if the voice
961 * is not NULL then it is a valid host voice.
962 */
963 return (phw != NULL);
964 }
965
966 if (!filter_conf.pDrv)
967 {
968 AssertFailed();
969 return (phw != NULL);
970 }
971
972 pVoice = (filterVoiceIn *)((uint8_t *)phw + filter_conf.pDrv->voice_size_in);
973
974 return pVoice->fHostOK;
975}
976
977int filteraudio_is_host_voice_out_ok(struct audio_driver *pDrv, HWVoiceOut *phw)
978{
979 /* Output is not yet implemented and there are no filter voices.
980 * The filter has no idea and assumes that if the voice
981 * is not NULL then it is a valid host voice.
982 *
983 * @todo: similar to filteraudio_is_host_voice_in_ok
984 */
985 NOREF(pDrv);
986 return (phw != NULL);
987}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use