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. */
|
---|
44 | typedef 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 */
|
---|
58 | typedef IORINGBUFFER* PIORINGBUFFER;
|
---|
59 |
|
---|
60 |
|
---|
61 | static 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 |
|
---|
82 | static void IORingBufferDestroy(PIORINGBUFFER pBuffer)
|
---|
83 | {
|
---|
84 | if (pBuffer)
|
---|
85 | {
|
---|
86 | if (pBuffer->pBuffer)
|
---|
87 | RTMemFree(pBuffer->pBuffer);
|
---|
88 | RTMemFree(pBuffer);
|
---|
89 | }
|
---|
90 | }
|
---|
91 |
|
---|
92 | DECL_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 |
|
---|
101 | DECL_FORCE_INLINE(uint32_t) IORingBufferFree(PIORINGBUFFER pBuffer)
|
---|
102 | {
|
---|
103 | AssertPtr(pBuffer);
|
---|
104 | return pBuffer->cBufSize - ASMAtomicReadU32(&pBuffer->cBufferUsed);
|
---|
105 | }
|
---|
106 |
|
---|
107 | DECL_FORCE_INLINE(uint32_t) IORingBufferUsed(PIORINGBUFFER pBuffer)
|
---|
108 | {
|
---|
109 | AssertPtr(pBuffer);
|
---|
110 | return ASMAtomicReadU32(&pBuffer->cBufferUsed);
|
---|
111 | }
|
---|
112 |
|
---|
113 | DECL_FORCE_INLINE(uint32_t) IORingBufferSize(PIORINGBUFFER pBuffer)
|
---|
114 | {
|
---|
115 | AssertPtr(pBuffer);
|
---|
116 | return pBuffer->cBufSize;
|
---|
117 | }
|
---|
118 |
|
---|
119 | static 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 |
|
---|
146 | DECL_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 |
|
---|
156 | static 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 |
|
---|
183 | DECL_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 |
|
---|
205 | struct
|
---|
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 | */
|
---|
223 | typedef 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 |
|
---|
245 | typedef 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 */
|
---|
286 | static int filteraudio_run_out(HWVoiceOut *hw);
|
---|
287 | static int filteraudio_write(SWVoiceOut *sw, void *buf, int len);
|
---|
288 | static int filteraudio_ctl_out(HWVoiceOut *hw, int cmd, ...);
|
---|
289 | static void filteraudio_fini_out(HWVoiceOut *hw);
|
---|
290 | static int filteraudio_init_out(HWVoiceOut *hw, audsettings_t *as);
|
---|
291 | static int caInitOutput(HWVoiceOut *hw);
|
---|
292 | static void caReinitOutput(HWVoiceOut *hw);
|
---|
293 |
|
---|
294 | static 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 |
|
---|
322 | static 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 |
|
---|
392 | static 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 |
|
---|
398 | static 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 |
|
---|
439 | static 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 |
|
---|
468 | static 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 | */
|
---|
520 | static 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 |
|
---|
583 | static 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 |
|
---|
663 | static 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 |
|
---|
669 | static 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 |
|
---|
772 | static 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 |
|
---|
820 | static 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 |
|
---|
881 | static 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 |
|
---|
889 | static 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 |
|
---|
902 | static 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 |
|
---|
917 | static 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 |
|
---|
933 | struct 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 |
|
---|
953 | int 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 |
|
---|
977 | int 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 | }
|
---|