VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostOSSAudio.cpp@ 57538

Last change on this file since 57538 was 57451, checked in by vboxsync, 9 years ago

Audio: Try to fix chopped off samples when playing short sound files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.6 KB
Line 
1/* $Id: DrvHostOSSAudio.cpp 57451 2015-08-19 09:39:17Z vboxsync $ */
2/** @file
3 * OSS (Open Sound System) host audio backend.
4 */
5
6/*
7 * Copyright (C) 2014-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 * --------------------------------------------------------------------
17 */
18#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
19#include <VBox/log.h>
20#include "DrvAudio.h"
21#include "AudioMixBuffer.h"
22
23#include "VBoxDD.h"
24
25#include <errno.h>
26#include <fcntl.h>
27#include <sys/ioctl.h>
28#include <sys/mman.h>
29#include <sys/soundcard.h>
30#include <unistd.h>
31
32#include <iprt/alloc.h>
33#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
34#include <VBox/vmm/pdmaudioifs.h>
35
36/**
37 * OSS host audio driver instance data.
38 * @implements PDMIAUDIOCONNECTOR
39 */
40typedef struct DRVHOSTOSSAUDIO
41{
42 /** Pointer to the driver instance structure. */
43 PPDMDRVINS pDrvIns;
44 /** Pointer to host audio interface. */
45 PDMIHOSTAUDIO IHostAudio;
46 /** Error count for not flooding the release log.
47 * UINT32_MAX for unlimited logging. */
48 uint32_t cLogErrors;
49} DRVHOSTOSSAUDIO, *PDRVHOSTOSSAUDIO;
50
51typedef struct OSSAUDIOSTREAMCFG
52{
53 PDMAUDIOFMT enmFormat;
54 PDMAUDIOENDIANNESS enmENDIANNESS;
55 uint16_t uFreq;
56 uint8_t cChannels;
57 uint16_t cFragments;
58 uint32_t cbFragmentSize;
59} OSSAUDIOSTREAMCFG, *POSSAUDIOSTREAMCFG;
60
61typedef struct OSSAUDIOSTREAMIN
62{
63 /** Note: Always must come first! */
64 PDMAUDIOHSTSTRMIN pStreamIn;
65 int hFile;
66 int cFragments;
67 int cbFragmentSize;
68 void *pvBuf;
69 size_t cbBuf;
70 int old_optr;
71} OSSAUDIOSTREAMIN, *POSSAUDIOSTREAMIN;
72
73typedef struct OSSAUDIOSTREAMOUT
74{
75 /** Note: Always must come first! */
76 PDMAUDIOHSTSTRMOUT pStreamOut;
77 int hFile;
78 int cFragments;
79 int cbFragmentSize;
80#ifndef RT_OS_L4
81 bool fMemMapped;
82#endif
83 void *pvPCMBuf;
84 int old_optr;
85} OSSAUDIOSTREAMOUT, *POSSAUDIOSTREAMOUT;
86
87typedef struct OSSAUDIOCFG
88{
89#ifndef RT_OS_L4
90 bool try_mmap;
91#endif
92 int nfrags;
93 int fragsize;
94 const char *devpath_out;
95 const char *devpath_in;
96 int debug;
97} OSSAUDIOCFG, *POSSAUDIOCFG;
98
99static OSSAUDIOCFG s_OSSConf =
100{
101#ifndef RT_OS_L4
102 false,
103#endif
104 4,
105 4096,
106 "/dev/dsp",
107 "/dev/dsp",
108 0
109};
110
111
112/* http://www.df.lth.se/~john_e/gems/gem002d.html */
113static uint32_t popcount(uint32_t u)
114{
115 u = ((u&0x55555555) + ((u>>1)&0x55555555));
116 u = ((u&0x33333333) + ((u>>2)&0x33333333));
117 u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
118 u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
119 u = ( u&0x0000ffff) + (u>>16);
120 return u;
121}
122
123static uint32_t lsbindex(uint32_t u)
124{
125 return popcount ((u&-u)-1);
126}
127
128static int drvHostOSSAudioFmtToOSS(PDMAUDIOFMT fmt)
129{
130 switch (fmt)
131 {
132 case AUD_FMT_S8:
133 return AFMT_S8;
134
135 case AUD_FMT_U8:
136 return AFMT_U8;
137
138 case AUD_FMT_S16:
139 return AFMT_S16_LE;
140
141 case AUD_FMT_U16:
142 return AFMT_U16_LE;
143
144 default:
145 break;
146 }
147
148 AssertMsgFailed(("Format %ld not supported\n", fmt));
149 return AFMT_U8;
150}
151
152static int drvHostOSSAudioOSSToFmt(int fmt,
153 PDMAUDIOFMT *pFmt, PDMAUDIOENDIANNESS *pENDIANNESS)
154{
155 switch (fmt)
156 {
157 case AFMT_S8:
158 *pFmt = AUD_FMT_S8;
159 if (pENDIANNESS)
160 *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
161 break;
162
163 case AFMT_U8:
164 *pFmt = AUD_FMT_U8;
165 if (pENDIANNESS)
166 *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
167 break;
168
169 case AFMT_S16_LE:
170 *pFmt = AUD_FMT_S16;
171 if (pENDIANNESS)
172 *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
173 break;
174
175 case AFMT_U16_LE:
176 *pFmt = AUD_FMT_U16;
177 if (pENDIANNESS)
178 *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
179 break;
180
181 case AFMT_S16_BE:
182 *pFmt = AUD_FMT_S16;
183 if (pENDIANNESS)
184 *pENDIANNESS = PDMAUDIOENDIANNESS_BIG;
185 break;
186
187 case AFMT_U16_BE:
188 *pFmt = AUD_FMT_U16;
189 if (pENDIANNESS)
190 *pENDIANNESS = PDMAUDIOENDIANNESS_BIG;
191 break;
192
193 default:
194 AssertMsgFailed(("Format %ld not supported\n", fmt));
195 return VERR_NOT_SUPPORTED;
196 }
197
198 return VINF_SUCCESS;
199}
200
201static int drvHostOSSAudioClose(int *phFile)
202{
203 if (!phFile || !*phFile)
204 return VINF_SUCCESS;
205
206 int rc;
207 if (close(*phFile))
208 {
209 LogRel(("OSS: Closing descriptor failed: %s\n",
210 strerror(errno)));
211 rc = VERR_GENERAL_FAILURE; /** @todo */
212 }
213 else
214 {
215 *phFile = -1;
216 rc = VINF_SUCCESS;
217 }
218
219 return rc;
220}
221
222static int drvHostOSSAudioOpen(bool fIn,
223 POSSAUDIOSTREAMCFG pReq, POSSAUDIOSTREAMCFG pObt,
224 int *phFile)
225{
226 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
227 AssertPtrReturn(pObt, VERR_INVALID_POINTER);
228 AssertPtrReturn(phFile, VERR_INVALID_POINTER);
229
230 int rc;
231 int hFile;
232
233 do
234 {
235 const char *pszDev = fIn ? s_OSSConf.devpath_in : s_OSSConf.devpath_out;
236 if (!pszDev)
237 {
238 LogRel(("OSS: Invalid or no %s device name set\n",
239 fIn ? "input" : "output"));
240 rc = VERR_INVALID_PARAMETER;
241 break;
242 }
243
244 hFile = open(pszDev, (fIn ? O_RDONLY : O_WRONLY) | O_NONBLOCK);
245 if (hFile == -1)
246 {
247 LogRel(("OSS: Failed to open %s: %s(%d)\n", pszDev, strerror(errno), errno));
248 rc = RTErrConvertFromErrno(errno);
249 break;
250 }
251
252 int iFormat = drvHostOSSAudioFmtToOSS(pReq->enmFormat);
253 if (ioctl(hFile, SNDCTL_DSP_SAMPLESIZE, &iFormat))
254 {
255 LogRel(("OSS: Failed to set audio format to %ld errno=%s(%d)\n", iFormat, strerror(errno), errno));
256 rc = RTErrConvertFromErrno(errno);
257 break;
258 }
259
260 int cChannels = pReq->cChannels;
261 if (ioctl(hFile, SNDCTL_DSP_CHANNELS, &cChannels))
262 {
263 LogRel(("OSS: Failed to set number of audio channels (%d): %s(%d)\n", pReq->cChannels, strerror(errno), errno));
264 rc = RTErrConvertFromErrno(errno);
265 break;
266 }
267
268 int freq = pReq->uFreq;
269 if (ioctl(hFile, SNDCTL_DSP_SPEED, &freq))
270 {
271 LogRel(("OSS: Failed to set audio frequency (%dHZ): %s(%d)\n", pReq->uFreq, strerror(errno), errno));
272 rc = RTErrConvertFromErrno(errno);
273 break;
274 }
275
276 /* Obsolete on Solaris (using O_NONBLOCK is sufficient). */
277#if !(defined(VBOX) && defined(RT_OS_SOLARIS))
278 if (ioctl(hFile, SNDCTL_DSP_NONBLOCK))
279 {
280 LogRel(("OSS: Failed to set non-blocking mode: %s(%d)\n", strerror(errno), errno));
281 rc = RTErrConvertFromErrno(errno);
282 break;
283 }
284#endif
285 int mmmmssss = (pReq->cFragments << 16) | lsbindex(pReq->cbFragmentSize);
286 if (ioctl(hFile, SNDCTL_DSP_SETFRAGMENT, &mmmmssss))
287 {
288 LogRel(("OSS: Failed to set %RU16 fragments to %RU32 bytes each: %s(%d)\n",
289 pReq->cFragments, pReq->cbFragmentSize, strerror(errno), errno));
290 rc = RTErrConvertFromErrno(errno);
291 break;
292 }
293
294 audio_buf_info abinfo;
295 if (ioctl(hFile, fIn ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo))
296 {
297 LogRel(("OSS: Failed to retrieve buffer length: %s(%d)\n", strerror(errno), errno));
298 rc = RTErrConvertFromErrno(errno);
299 break;
300 }
301
302 rc = drvHostOSSAudioOSSToFmt(iFormat, &pObt->enmFormat, &pObt->enmENDIANNESS);
303 if (RT_SUCCESS(rc))
304 {
305 pObt->cChannels = cChannels;
306 pObt->uFreq = freq;
307 pObt->cFragments = abinfo.fragstotal;
308 pObt->cbFragmentSize = abinfo.fragsize;
309
310 *phFile = hFile;
311 }
312 }
313 while (0);
314
315 if (RT_FAILURE(rc))
316 drvHostOSSAudioClose(&hFile);
317
318 LogFlowFuncLeaveRC(rc);
319 return rc;
320}
321
322static DECLCALLBACK(int) drvHostOSSAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
323 PDMAUDIOSTREAMCMD enmStreamCmd)
324{
325 NOREF(pInterface);
326 NOREF(pHstStrmIn);
327 NOREF(enmStreamCmd);
328
329 /** @todo Nothing to do here right now!? */
330
331 return VINF_SUCCESS;
332}
333
334static DECLCALLBACK(int) drvHostOSSAudioControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
335 PDMAUDIOSTREAMCMD enmStreamCmd)
336{
337 NOREF(pInterface);
338 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
339
340 POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
341
342#ifdef RT_OS_L4
343 return VINF_SUCCESS;
344#else
345 if (!pThisStrmOut->fMemMapped)
346 return VINF_SUCCESS;
347#endif
348
349 int rc = VINF_SUCCESS;
350 int mask;
351 switch (enmStreamCmd)
352 {
353 case PDMAUDIOSTREAMCMD_ENABLE:
354 {
355 drvAudioClearBuf(&pHstStrmOut->Props,
356 pThisStrmOut->pvPCMBuf, AudioMixBufSize(&pHstStrmOut->MixBuf));
357
358 mask = PCM_ENABLE_OUTPUT;
359 if (ioctl(pThisStrmOut->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
360 {
361 LogRel(("OSS: Failed to enable output stream: %s\n", strerror(errno)));
362 rc = RTErrConvertFromErrno(errno);
363 }
364
365 break;
366 }
367
368 case PDMAUDIOSTREAMCMD_DISABLE:
369 {
370 mask = 0;
371 if (ioctl(pThisStrmOut->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
372 {
373 LogRel(("OSS: Failed to disable output stream: %s\n", strerror(errno)));
374 rc = RTErrConvertFromErrno(errno);
375 }
376
377 break;
378 }
379
380 default:
381 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
382 rc = VERR_INVALID_PARAMETER;
383 break;
384 }
385
386 LogFlowFuncLeaveRC(rc);
387 return rc;
388}
389
390static DECLCALLBACK(int) drvHostOSSAudioInit(PPDMIHOSTAUDIO pInterface)
391{
392 NOREF(pInterface);
393
394 LogFlowFuncEnter();
395
396 return VINF_SUCCESS;
397}
398
399static DECLCALLBACK(int) drvHostOSSAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
400 uint32_t *pcSamplesCaptured)
401{
402 NOREF(pInterface);
403 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
404
405 POSSAUDIOSTREAMIN pThisStrmIn = (POSSAUDIOSTREAMIN)pHstStrmIn;
406
407 int rc = VINF_SUCCESS;
408 size_t cbToRead = RT_MIN(pThisStrmIn->cbBuf,
409 AudioMixBufFreeBytes(&pHstStrmIn->MixBuf));
410
411 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
412
413 uint32_t cWrittenTotal = 0;
414 uint32_t cbTemp;
415 ssize_t cbRead;
416 size_t offWrite = 0;
417
418 while (cbToRead)
419 {
420 cbTemp = RT_MIN(cbToRead, pThisStrmIn->cbBuf);
421 AssertBreakStmt(cbTemp, rc = VERR_NO_DATA);
422 cbRead = read(pThisStrmIn->hFile, (uint8_t *)pThisStrmIn->pvBuf + offWrite, cbTemp);
423
424 LogFlowFunc(("cbRead=%zi, cbTemp=%RU32, cbToRead=%zu\n",
425 cbRead, cbTemp, cbToRead));
426
427 if (cbRead < 0)
428 {
429 switch (errno)
430 {
431 case 0:
432 {
433 LogFunc(("Failed to read %z frames\n", cbRead));
434 rc = VERR_ACCESS_DENIED;
435 break;
436 }
437
438 case EINTR:
439 case EAGAIN:
440 rc = VERR_NO_DATA;
441 break;
442
443 default:
444 LogFlowFunc(("Failed to read %zu input frames, rc=%Rrc\n",
445 cbTemp, rc));
446 rc = VERR_GENERAL_FAILURE; /** @todo */
447 break;
448 }
449
450 if (RT_FAILURE(rc))
451 break;
452 }
453 else if (cbRead)
454 {
455 uint32_t cWritten;
456 rc = AudioMixBufWriteCirc(&pHstStrmIn->MixBuf,
457 pThisStrmIn->pvBuf, cbRead,
458 &cWritten);
459 if (RT_FAILURE(rc))
460 break;
461
462 uint32_t cbWritten = AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cWritten);
463
464 Assert(cbToRead >= cbWritten);
465 cbToRead -= cbWritten;
466 offWrite += cbWritten;
467 cWrittenTotal += cWritten;
468 }
469 else /* No more data, try next round. */
470 break;
471 }
472
473 if (rc == VERR_NO_DATA)
474 rc = VINF_SUCCESS;
475
476 if (RT_SUCCESS(rc))
477 {
478 uint32_t cProcessed = 0;
479 if (cWrittenTotal)
480 rc = AudioMixBufMixToParent(&pHstStrmIn->MixBuf, cWrittenTotal,
481 &cProcessed);
482
483 if (pcSamplesCaptured)
484 *pcSamplesCaptured = cWrittenTotal;
485
486 LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 processed), rc=%Rrc\n",
487 cWrittenTotal, cProcessed, rc));
488 }
489
490 LogFlowFuncLeaveRC(rc);
491 return rc;
492}
493
494static DECLCALLBACK(int) drvHostOSSAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
495{
496 NOREF(pInterface);
497 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
498
499 POSSAUDIOSTREAMIN pThisStrmIn = (POSSAUDIOSTREAMIN)pHstStrmIn;
500
501 LogFlowFuncEnter();
502
503 if (pThisStrmIn->pvBuf)
504 {
505 RTMemFree(pThisStrmIn->pvBuf);
506 pThisStrmIn->pvBuf = NULL;
507 }
508
509 return VINF_SUCCESS;
510}
511
512static DECLCALLBACK(int) drvHostOSSAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
513{
514 NOREF(pInterface);
515 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
516
517 POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
518
519 LogFlowFuncEnter();
520
521#ifndef RT_OS_L4
522 if (!pThisStrmOut->fMemMapped)
523 {
524 if (pThisStrmOut->pvPCMBuf)
525 {
526 RTMemFree(pThisStrmOut->pvPCMBuf);
527 pThisStrmOut->pvPCMBuf = NULL;
528 }
529 }
530#endif
531
532 return VINF_SUCCESS;
533}
534
535static DECLCALLBACK(int) drvHostOSSAudioGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
536{
537 NOREF(pInterface);
538
539 pCfg->cbStreamOut = sizeof(OSSAUDIOSTREAMOUT);
540 pCfg->cbStreamIn = sizeof(OSSAUDIOSTREAMIN);
541 pCfg->cMaxHstStrmsOut = INT_MAX;
542 pCfg->cMaxHstStrmsIn = INT_MAX;
543
544 return VINF_SUCCESS;
545}
546
547static DECLCALLBACK(int) drvHostOSSAudioInitIn(PPDMIHOSTAUDIO pInterface,
548 PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
549 PDMAUDIORECSOURCE enmRecSource,
550 uint32_t *pcSamples)
551{
552 NOREF(pInterface);
553 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
554 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
555
556 POSSAUDIOSTREAMIN pThisStrmIn = (POSSAUDIOSTREAMIN)pHstStrmIn;
557
558 int rc;
559 int hFile = -1;
560
561 do
562 {
563 uint32_t cSamples;
564
565 OSSAUDIOSTREAMCFG reqStream, obtStream;
566 reqStream.enmFormat = pCfg->enmFormat;
567 reqStream.uFreq = pCfg->uHz;
568 reqStream.cChannels = pCfg->cChannels;
569 reqStream.cFragments = s_OSSConf.nfrags;
570 reqStream.cbFragmentSize = s_OSSConf.fragsize;
571
572 rc = drvHostOSSAudioOpen(true /* fIn */,
573 &reqStream, &obtStream, &hFile);
574 if (RT_SUCCESS(rc))
575 {
576 if (obtStream.cFragments * obtStream.cbFragmentSize & pHstStrmIn->Props.uAlign)
577 LogRel(("OSS: Warning: Misaligned DAC output buffer: Size = %zu, Alignment = %u\n",
578 obtStream.cFragments * obtStream.cbFragmentSize,
579 pHstStrmIn->Props.uAlign + 1));
580
581 pThisStrmIn->hFile = hFile;
582
583 PDMAUDIOSTREAMCFG streamCfg;
584 streamCfg.enmFormat = obtStream.enmFormat;
585 streamCfg.uHz = obtStream.uFreq;
586 streamCfg.cChannels = pCfg->cChannels;
587 streamCfg.enmEndianness = obtStream.enmENDIANNESS;
588
589 rc = drvAudioStreamCfgToProps(&streamCfg, &pHstStrmIn->Props);
590 if (RT_SUCCESS(rc))
591 {
592 cSamples = (obtStream.cFragments * obtStream.cbFragmentSize)
593 >> pHstStrmIn->Props.cShift;
594 if (!cSamples)
595 rc = VERR_INVALID_PARAMETER;
596 }
597 }
598
599 if (RT_SUCCESS(rc))
600 {
601 size_t cbBuf = cSamples * (1 << pHstStrmIn->Props.cShift);
602 pThisStrmIn->pvBuf = RTMemAlloc(cbBuf);
603 if (!pThisStrmIn->pvBuf)
604 {
605 LogRel(("OSS: Failed allocating ADC buffer with %RU32 samples, each %d bytes\n",
606 cSamples, 1 << pHstStrmIn->Props.cShift));
607 rc = VERR_NO_MEMORY;
608 }
609
610 pThisStrmIn->cbBuf = cbBuf;
611
612 if (pcSamples)
613 *pcSamples = cSamples;
614 }
615
616 } while (0);
617
618 if (RT_FAILURE(rc))
619 drvHostOSSAudioClose(&hFile);
620
621 LogFlowFuncLeaveRC(rc);
622 return rc;
623}
624
625static DECLCALLBACK(int) drvHostOSSAudioInitOut(PPDMIHOSTAUDIO pInterface,
626 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
627 uint32_t *pcSamples)
628{
629 NOREF(pInterface);
630 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
631 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
632
633 POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
634
635 int rc;
636 int hFile = -1;
637
638 do
639 {
640 uint32_t cSamples;
641
642 OSSAUDIOSTREAMCFG reqStream, obtStream;
643 reqStream.enmFormat = pCfg->enmFormat;
644 reqStream.uFreq = pCfg->uHz;
645 reqStream.cChannels = pCfg->cChannels;
646 reqStream.cFragments = s_OSSConf.nfrags;
647 reqStream.cbFragmentSize = s_OSSConf.fragsize;
648
649 rc = drvHostOSSAudioOpen(false /* fIn */,
650 &reqStream, &obtStream, &hFile);
651 if (RT_SUCCESS(rc))
652 {
653 if (obtStream.cFragments * obtStream.cbFragmentSize & pHstStrmOut->Props.uAlign)
654 LogRel(("OSS: Warning: Misaligned DAC output buffer: Size = %zu, Alignment = %u\n",
655 obtStream.cFragments * obtStream.cbFragmentSize,
656 pHstStrmOut->Props.uAlign + 1));
657
658 pThisStrmOut->hFile = hFile;
659
660 PDMAUDIOSTREAMCFG streamCfg;
661 streamCfg.enmFormat = obtStream.enmFormat;
662 streamCfg.uHz = obtStream.uFreq;
663 streamCfg.cChannels = pCfg->cChannels;
664 streamCfg.enmEndianness = obtStream.enmENDIANNESS;
665
666 rc = drvAudioStreamCfgToProps(&streamCfg, &pHstStrmOut->Props);
667 if (RT_SUCCESS(rc))
668 cSamples = (obtStream.cFragments * obtStream.cbFragmentSize)
669 >> pHstStrmOut->Props.cShift;
670 }
671
672 if (RT_SUCCESS(rc))
673 {
674#ifndef RT_OS_L4
675 pThisStrmOut->fMemMapped = false;
676 if (s_OSSConf.try_mmap)
677 {
678 pThisStrmOut->pvPCMBuf = mmap(0, cSamples << pHstStrmOut->Props.cShift,
679 PROT_READ | PROT_WRITE, MAP_SHARED, hFile, 0);
680 if (pThisStrmOut->pvPCMBuf == MAP_FAILED)
681 {
682 LogRel(("OSS: Failed to memory map %zu bytes of DAC output file: %s\n",
683 cSamples << pHstStrmOut->Props.cShift, strerror(errno)));
684 rc = RTErrConvertFromErrno(errno);
685 break;
686 }
687 else
688 {
689 int mask = 0;
690 if (ioctl(hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
691 {
692 LogRel(("OSS: Failed to retrieve initial trigger mask: %s\n",
693 strerror(errno)));
694 rc = RTErrConvertFromErrno(errno);
695 /* Note: No break here, need to unmap file first! */
696 }
697 else
698 {
699 mask = PCM_ENABLE_OUTPUT;
700 if (ioctl (hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
701 {
702 LogRel(("OSS: Failed to retrieve PCM_ENABLE_OUTPUT mask: %s\n",
703 strerror(errno)));
704 rc = RTErrConvertFromErrno(errno);
705 /* Note: No break here, need to unmap file first! */
706 }
707 else
708 pThisStrmOut->fMemMapped = true;
709 }
710
711 if (!pThisStrmOut->fMemMapped)
712 {
713 int rc2 = munmap(pThisStrmOut->pvPCMBuf,
714 cSamples << pHstStrmOut->Props.cShift);
715 if (rc2)
716 LogRel(("OSS: Failed to unmap DAC output file: %s\n",
717 strerror(errno)));
718
719 break;
720 }
721 }
722 }
723#endif /* !RT_OS_L4 */
724
725 /* Memory mapping failed above? Try allocating an own buffer. */
726#ifndef RT_OS_L4
727 if (!pThisStrmOut->fMemMapped)
728 {
729#endif
730 LogFlowFunc(("cSamples=%RU32\n", cSamples));
731 pThisStrmOut->pvPCMBuf = RTMemAlloc(cSamples * (1 << pHstStrmOut->Props.cShift));
732 if (!pThisStrmOut->pvPCMBuf)
733 {
734 LogRel(("OSS: Failed allocating DAC buffer with %RU32 samples, each %d bytes\n",
735 cSamples, 1 << pHstStrmOut->Props.cShift));
736 rc = VERR_NO_MEMORY;
737 break;
738 }
739#ifndef RT_OS_L4
740 }
741#endif
742 if (pcSamples)
743 *pcSamples = cSamples;
744 }
745
746 } while (0);
747
748 if (RT_FAILURE(rc))
749 drvHostOSSAudioClose(&hFile);
750
751 LogFlowFuncLeaveRC(rc);
752 return rc;
753}
754
755static DECLCALLBACK(bool) drvHostOSSAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
756{
757 NOREF(pInterface);
758 NOREF(enmDir);
759 return true; /* Always all enabled. */
760}
761
762static DECLCALLBACK(int) drvHostOSSAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
763 uint32_t *pcSamplesPlayed)
764{
765 NOREF(pInterface);
766 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
767
768 POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
769
770 int rc = VINF_SUCCESS;
771 uint32_t cbReadTotal = 0;
772 count_info cntinfo;
773
774 do
775 {
776 size_t cbBuf = AudioMixBufSizeBytes(&pHstStrmOut->MixBuf);
777
778 uint32_t cLive = drvAudioHstOutSamplesLive(pHstStrmOut);
779 uint32_t cToRead;
780
781#ifndef RT_OS_L4
782 if (pThisStrmOut->fMemMapped)
783 {
784 /* Get current playback pointer. */
785 int rc2 = ioctl(pThisStrmOut->hFile, SNDCTL_DSP_GETOPTR, &cntinfo);
786 if (!rc2)
787 {
788 LogRel(("OSS: Failed to retrieve current playback pointer: %s\n",
789 strerror(errno)));
790 rc = RTErrConvertFromErrno(errno);
791 break;
792 }
793
794 /* Nothing to play? */
795 if (cntinfo.ptr == pThisStrmOut->old_optr)
796 break;
797
798 int cbData;
799 if (cntinfo.ptr > pThisStrmOut->old_optr)
800 cbData = cntinfo.ptr - pThisStrmOut->old_optr;
801 else
802 cbData = cbBuf + cntinfo.ptr - pThisStrmOut->old_optr;
803 Assert(cbData);
804
805 cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbData),
806 cLive);
807 }
808 else
809 {
810#endif
811 audio_buf_info abinfo;
812 int rc2 = ioctl(pThisStrmOut->hFile, SNDCTL_DSP_GETOSPACE, &abinfo);
813 if (rc2 < 0)
814 {
815 LogRel(("OSS: Failed to retrieve current playback buffer: %s\n",
816 strerror(errno)));
817 rc = RTErrConvertFromErrno(errno);
818 break;
819 }
820
821 if ((size_t)abinfo.bytes > cbBuf)
822 {
823 LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%d\n",
824 abinfo.bytes, cbBuf));
825 abinfo.bytes = cbBuf;
826 /* Keep going. */
827 }
828
829 if (abinfo.bytes < 0)
830 {
831 LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%d\n",
832 abinfo.bytes, cbBuf));
833 rc = VERR_INVALID_PARAMETER;
834 break;
835 }
836
837 cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, abinfo.bytes),
838 cLive);
839 if (!cToRead)
840 break;
841#ifndef RT_OS_L4
842 }
843#endif
844 size_t cbToRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cToRead);
845 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
846
847 uint32_t cRead, cbRead;
848 while (cbToRead)
849 {
850 rc = AudioMixBufReadCirc(&pHstStrmOut->MixBuf,
851 pThisStrmOut->pvPCMBuf, cbToRead, &cRead);
852 if (RT_FAILURE(rc))
853 break;
854
855 cbRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead);
856 ssize_t cbWritten = write(pThisStrmOut->hFile, pThisStrmOut->pvPCMBuf,
857 cbRead);
858 if (cbWritten == -1)
859 {
860 LogRel(("OSS: Failed writing output data %s\n", strerror(errno)));
861 rc = RTErrConvertFromErrno(errno);
862 break;
863 }
864
865 Assert(cbToRead >= cbRead);
866 cbToRead -= cbRead;
867 cbReadTotal += cbRead;
868 }
869
870#ifndef RT_OS_L4
871 /* Update read pointer. */
872 if (pThisStrmOut->fMemMapped)
873 pThisStrmOut->old_optr = cntinfo.ptr;
874#endif
875
876 } while(0);
877
878 if (RT_SUCCESS(rc))
879 {
880 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbReadTotal);
881 if (cReadTotal)
882 AudioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
883
884 if (pcSamplesPlayed)
885 *pcSamplesPlayed = cReadTotal;
886
887 LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes), rc=%Rrc\n",
888 cReadTotal, cbReadTotal, rc));
889 }
890
891 LogFlowFuncLeaveRC(rc);
892 return rc;
893}
894
895static DECLCALLBACK(void) drvHostOSSAudioShutdown(PPDMIHOSTAUDIO pInterface)
896{
897 NOREF(pInterface);
898}
899
900/**
901 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
902 */
903static DECLCALLBACK(void *) drvHostOSSAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
904{
905 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
906 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
907 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
908 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
909
910 return NULL;
911}
912
913/**
914 * Constructs an OSS audio driver instance.
915 *
916 * @copydoc FNPDMDRVCONSTRUCT
917 */
918static DECLCALLBACK(int) drvHostOSSAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
919{
920 PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
921 LogRel(("Audio: Initializing OSS driver\n"));
922
923 /*
924 * Init the static parts.
925 */
926 pThis->pDrvIns = pDrvIns;
927 /* IBase */
928 pDrvIns->IBase.pfnQueryInterface = drvHostOSSAudioQueryInterface;
929 /* IHostAudio */
930 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostOSSAudio);
931
932 return VINF_SUCCESS;
933}
934
935/**
936 * Char driver registration record.
937 */
938const PDMDRVREG g_DrvHostOSSAudio =
939{
940 /* u32Version */
941 PDM_DRVREG_VERSION,
942 /* szName */
943 "OSSAudio",
944 /* szRCMod */
945 "",
946 /* szR0Mod */
947 "",
948 /* pszDescription */
949 "OSS audio host driver",
950 /* fFlags */
951 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
952 /* fClass. */
953 PDM_DRVREG_CLASS_AUDIO,
954 /* cMaxInstances */
955 ~0U,
956 /* cbInstance */
957 sizeof(DRVHOSTOSSAUDIO),
958 /* pfnConstruct */
959 drvHostOSSAudioConstruct,
960 /* pfnDestruct */
961 NULL,
962 /* pfnRelocate */
963 NULL,
964 /* pfnIOCtl */
965 NULL,
966 /* pfnPowerOn */
967 NULL,
968 /* pfnReset */
969 NULL,
970 /* pfnSuspend */
971 NULL,
972 /* pfnResume */
973 NULL,
974 /* pfnAttach */
975 NULL,
976 /* pfnDetach */
977 NULL,
978 /* pfnPowerOff */
979 NULL,
980 /* pfnSoftReset */
981 NULL,
982 /* u32EndVersion */
983 PDM_DRVREG_VERSION
984};
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette