Changeset 64610 in vbox for trunk/src/VBox/Devices
- Timestamp:
- Nov 9, 2016 11:55:09 AM (8 years ago)
- svn:sync-xref-src-repo-rev:
- 111827
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/DrvHostCoreAudio.cpp
r64426 r64610 38 38 #include <AudioUnit/AudioUnit.h> 39 39 #include <AudioToolbox/AudioConverter.h> 40 #ifdef VBOX_WITH_AUDIO_CA_QUEUES 41 # include <AudioToolbox/AudioToolbox.h> 42 #endif 40 #include <AudioToolbox/AudioToolbox.h> 43 41 44 42 #if 0 … … 258 256 #endif /* !VBOX_WITH_AUDIO_CALLBACKS */ 259 257 260 #ifndef VBOX_WITH_AUDIO_CA_QUEUES261 static OSStatus coreAudioSetFrameBufferSize(AudioDeviceID deviceID, bool fInput, UInt32 cReqSize, UInt32 *pcActSize)262 {263 AudioObjectPropertyScope propScope = fInput264 ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;265 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyBufferFrameSize, propScope,266 kAudioObjectPropertyElementMaster };267 268 /* First try to set the new frame buffer size. */269 OSStatus err = AudioObjectSetPropertyData(deviceID, &propAdr, 0, NULL, sizeof(cReqSize), &cReqSize);270 271 /* Check if it really was set. */272 UInt32 cSize = sizeof(*pcActSize);273 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &cSize, pcActSize);274 if (RT_UNLIKELY(err != noErr))275 return err;276 277 /* If both sizes are the same, we are done. */278 if (cReqSize == *pcActSize)279 return noErr;280 281 /* If not we have to check the limits of the device. First get the size of282 the buffer size range property. */283 propAdr.mSelector = kAudioDevicePropertyBufferSizeRange;284 err = AudioObjectGetPropertyDataSize(deviceID, &propAdr, 0, NULL, &cSize);285 if (RT_UNLIKELY(err != noErr))286 return err;287 288 Assert(cSize);289 AudioValueRange *pRange = (AudioValueRange *)RTMemAllocZ(cSize);290 if (pRange)291 {292 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &cSize, pRange);293 if (err == noErr)294 {295 Float64 cMin = -1;296 Float64 cMax = -1;297 for (size_t a = 0; a < cSize / sizeof(AudioValueRange); a++)298 {299 /* Search for the absolute minimum. */300 if ( pRange[a].mMinimum < cMin301 || cMin == -1)302 cMin = pRange[a].mMinimum;303 304 /* Search for the best maximum which isn't bigger than cReqSize. */305 if (pRange[a].mMaximum < cReqSize)306 {307 if (pRange[a].mMaximum > cMax)308 cMax = pRange[a].mMaximum;309 }310 }311 if (cMax == -1)312 cMax = cMin;313 cReqSize = cMax;314 315 /* First try to set the new frame buffer size. */316 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;317 err = AudioObjectSetPropertyData(deviceID, &propAdr, 0, NULL, sizeof(cReqSize), &cReqSize);318 if (err == noErr)319 {320 /* Check if it really was set. */321 cSize = sizeof(*pcActSize);322 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &cSize, pcActSize);323 }324 }325 326 RTMemFree(pRange);327 }328 else329 err = notEnoughMemoryErr;330 331 return err;332 }333 #endif334 335 258 #if 0 /* unused */ 336 259 static int coreAudioCFStringToCString(const CFStringRef pCFString, char **ppszString) … … 489 412 /** The stream's direction. */ 490 413 PDMAUDIODIR enmDir; 491 #ifdef VBOX_WITH_AUDIO_CA_QUEUES492 414 /** The stream's thread handle for maintaining the audio queue. */ 493 415 RTTHREAD hThread; … … 508 430 /** The acquired (final) audio format for this stream. */ 509 431 AudioStreamBasicDescription asbdStream; 510 #endif511 432 /** The audio unit for this stream. */ 512 433 COREAUDIOUNIT Unit; … … 524 445 static int coreAudioStreamUninit(PCOREAUDIOSTREAM pCAStream); 525 446 526 #ifndef VBOX_WITH_AUDIO_CA_QUEUES527 447 static int coreAudioStreamInitIn(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq); 528 448 static int coreAudioStreamInitOut(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq); 529 #endif530 449 531 450 static int coreAudioStreamControl(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, PDMAUDIOSTREAMCMD enmStreamCmd); … … 537 456 static OSStatus coreAudioDevPropChgCb(AudioObjectID propertyID, UInt32 nAddresses, const AudioObjectPropertyAddress properties[], void *pvUser); 538 457 539 #ifndef VBOX_WITH_AUDIO_CA_QUEUES540 static OSStatus coreAudioPlaybackCb(void *pvUser, AudioUnitRenderActionFlags *pActionFlags, const AudioTimeStamp *pAudioTS, UInt32 uBusID, UInt32 cFrames, AudioBufferList* pBufData);541 #else542 458 static int coreAudioStreamInitQueue(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq); 543 459 static void coreAudioInputQueueCb(void *pvUser, AudioQueueRef audioQueue, AudioQueueBufferRef audioBuffer, const AudioTimeStamp *pAudioTS, UInt32 cPacketDesc, const AudioStreamPacketDescription *paPacketDesc); 544 460 static void coreAudioOutputQueueCb(void *pvUser, AudioQueueRef audioQueue, AudioQueueBufferRef audioBuffer); 545 #endif546 547 #ifndef VBOX_WITH_AUDIO_CA_QUEUES548 /**549 * Returns whether an assigned audio unit to a given stream is running or not.550 *551 * @return True if audio unit is running, false if not.552 * @param pStream Audio stream to check.553 */554 static bool coreAudioUnitIsRunning(PCOREAUDIOSTREAM pStream)555 {556 AssertPtrReturn(pStream, false);557 558 UInt32 uFlag = 0;559 UInt32 uSize = sizeof(uFlag);560 OSStatus err = AudioUnitGetProperty(pStream->Unit.audioUnit, kAudioOutputUnitProperty_IsRunning, kAudioUnitScope_Global,561 0, &uFlag, &uSize);562 if (err != kAudioHardwareNoError)563 LogRel(("CoreAudio: Could not determine whether the audio unit is running (%RI32)\n", err));564 565 Log3Func(("%s -> %RU32\n", pStream->enmDir == PDMAUDIODIR_IN ? "Input" : "Output", uFlag));566 567 return (uFlag >= 1);568 }569 #endif /* !VBOX_WITH_AUDIO_CA_QUEUES */570 461 571 462 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER … … 1252 1143 if (RT_SUCCESS(rc)) 1253 1144 { 1254 #ifdef VBOX_WITH_AUDIO_CA_QUEUES1255 1145 rc = coreAudioStreamInitQueue(pCAStream, pCfg /* pCfgReq */, NULL /* pCfgAcq */); 1256 #else1257 if (pCAStream->enmDir == PDMAUDIODIR_IN)1258 rc = coreAudioStreamInitIn (pCAStream, pCfg /* pCfgReq */, NULL /* pCfgAcq */);1259 else1260 rc = coreAudioStreamInitOut(pCAStream, pCfg /* pCfgReq */, NULL /* pCfgAcq */);1261 #endif1262 1146 if (RT_SUCCESS(rc)) 1263 1147 rc = coreAudioStreamControl(pCAStream->pDrv, pCAStream, PDMAUDIOSTREAMCMD_ENABLE); … … 1390 1274 #endif /* VBOX_WITH_AUDIO_CA_CONVERTER */ 1391 1275 1392 #ifndef VBOX_WITH_AUDIO_CA_QUEUES1393 /* Callback to feed audio input buffer. */1394 static DECLCALLBACK(OSStatus) coreAudioCaptureCb(void *pvUser,1395 AudioUnitRenderActionFlags *pActionFlags,1396 const AudioTimeStamp *pAudioTS,1397 UInt32 uBusID,1398 UInt32 cFrames,1399 AudioBufferList *pBufData)1400 {1401 RT_NOREF(uBusID, pBufData);1402 1403 /* If nothing is pending return immediately. */1404 if (cFrames == 0)1405 return noErr;1406 1407 PCOREAUDIOSTREAM pStream = (PCOREAUDIOSTREAM)pvUser;1408 1409 /* Sanity. */1410 AssertPtr(pStream);1411 AssertPtr(pStream->pDrv);1412 Assert (pStream->enmDir == PDMAUDIODIR_IN);1413 AssertPtr(pStream->Unit.pDevice);1414 1415 if (ASMAtomicReadU32(&pStream->enmStatus) != COREAUDIOSTATUS_INIT)1416 return noErr;1417 1418 OSStatus err = noErr;1419 int rc = VINF_SUCCESS;1420 1421 AudioBufferList srcBufLst;1422 RT_ZERO(srcBufLst);1423 1424 do1425 {1426 #ifdef DEBUG1427 AudioStreamBasicDescription asbdIn;1428 UInt32 uSize = sizeof(asbdIn);1429 AudioUnitGetProperty(pStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,1430 1, &asbdIn, &uSize);1431 coreAudioPrintASBD("DevIn", &asbdIn);1432 1433 AudioStreamBasicDescription asbdOut;1434 uSize = sizeof(asbdOut);1435 AudioUnitGetProperty(pStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,1436 1, &asbdOut, &uSize);1437 coreAudioPrintASBD("DevOut", &asbdOut);1438 #endif1439 1440 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER1441 /* Are we using a converter? */1442 if (pStreamIn->ConverterRef)1443 {1444 PCOREAUDIOCONVCBCTX pConvCbCtx = &pStreamIn->convCbCtx;1445 1446 AudioStreamBasicDescription *pSrcASBD = &pConvCbCtx->asbdSrc;1447 AudioStreamBasicDescription *pDstASBD = &pConvCbCtx->asbdDst;1448 # ifdef DEBUG1449 coreAudioPrintASBD("Src", pSrcASBD);1450 coreAudioPrintASBD("Dst", pDstASBD);1451 # endif1452 /* Initialize source list buffer. */1453 srcBufLst.mNumberBuffers = 1;1454 1455 /* Initialize the first buffer. */1456 srcBufLst.mBuffers[0].mNumberChannels = pSrcASBD->mChannelsPerFrame;1457 srcBufLst.mBuffers[0].mDataByteSize = pSrcASBD->mBytesPerFrame * cFrames;1458 srcBufLst.mBuffers[0].mData = RTMemAllocZ(srcBufLst.mBuffers[0].mDataByteSize);1459 if (!srcBufLst.mBuffers[0].mData)1460 {1461 rc = VERR_NO_MEMORY;1462 break;1463 }1464 1465 #if 01466 /* Initialize the second buffer. */1467 srcBufLst.mBuffers[1].mNumberChannels = 1; //pSrcASBD->mChannelsPerFrame;1468 srcBufLst.mBuffers[1].mDataByteSize = pSrcASBD->mBytesPerFrame * cFrames;1469 srcBufLst.mBuffers[1].mData = RTMemAllocZ(srcBufLst.mBuffers[1].mDataByteSize);1470 if (!srcBufLst.mBuffers[1].mData)1471 {1472 rc = VERR_NO_MEMORY;1473 break;1474 }1475 #endif1476 1477 /* Set the buffer list for our callback context. */1478 pConvCbCtx->pBufLstSrc = &srcBufLst;1479 1480 /* Sanity. */1481 AssertPtr(pConvCbCtx->pBufLstSrc);1482 Assert(pConvCbCtx->pBufLstSrc->mNumberBuffers >= 1);1483 1484 /* Get the first buffer. */1485 AudioBuffer *pSrcBuf = &srcBufLst.mBuffers[0];1486 1487 Log3Func(("pSrcBuf->mDataByteSize1=%RU32\n", pSrcBuf->mDataByteSize));1488 1489 /* First, render the source data as usual. */1490 err = AudioUnitRender(pStreamIn->audioUnit, pActionFlags, pAudioTS, uBusID, cFrames,1491 &srcBufLst);1492 if (err != noErr)1493 {1494 if (pConvCbCtx->cErrors < 32) /** @todo Make this configurable. */1495 {1496 LogRel2(("CoreAudio: Failed rendering converted audio input data (%RI32:%c%c%c%c)\n", err,1497 RT_BYTE4(err), RT_BYTE3(err), RT_BYTE2(err), RT_BYTE1(err)));1498 pConvCbCtx->cErrors++;1499 }1500 1501 rc = VERR_IO_GEN_FAILURE;1502 break;1503 }1504 1505 /* Note: pSrcBuf->mDataByteSize can have changed after calling AudioUnitRender above! */1506 Log3Func(("pSrcBuf->mDataByteSize2=%RU32\n", pSrcBuf->mDataByteSize));1507 1508 # ifdef DEBUG_DUMP_PCM_DATA1509 RTFILE fh;1510 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-recording-cb-src.pcm",1511 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);1512 if (RT_SUCCESS(rc))1513 {1514 RTFileWrite(fh, pSrcBuf->mData, pSrcBuf->mDataByteSize, NULL);1515 RTFileClose(fh);1516 }1517 else1518 AssertFailed();1519 # endif1520 AudioBufferList dstBufLst;1521 RT_ZERO(dstBufLst);1522 1523 dstBufLst.mNumberBuffers = 1; /* We only use one buffer at once. */1524 1525 AudioBuffer *pDstBuf = &dstBufLst.mBuffers[0];1526 1527 UInt32 cbDst = pDstASBD->mBytesPerFrame * cFrames;1528 void *pvDst = RTMemAlloc(cbDst);1529 if (!pvDst)1530 {1531 rc = VERR_NO_MEMORY;1532 break;1533 }1534 1535 pDstBuf->mDataByteSize = cbDst;1536 pDstBuf->mData = pvDst;1537 1538 AudioConverterReset(pStreamIn->ConverterRef);1539 1540 Log3Func(("cbSrcBufSize=%RU32 (BPF=%RU32), cbDstBufSize=%RU32 (BPF=%RU32)\n",1541 pSrcBuf->mDataByteSize, pSrcASBD->mBytesPerFrame,1542 pDstBuf->mDataByteSize, pDstASBD->mBytesPerFrame));1543 1544 if (pSrcASBD->mSampleRate == pDstASBD->mSampleRate)1545 {1546 err = AudioConverterConvertBuffer(pStreamIn->ConverterRef,1547 #if 01548 cbT1, pvT1, &cbT2, pvT2);1549 #else1550 pSrcBuf->mDataByteSize, pSrcBuf->mData /* Input */,1551 &cbDst, pvDst /* Output */);1552 #endif1553 if (err != noErr)1554 {1555 if (pConvCbCtx->cErrors < 32) /** @todo Make this configurable. */1556 {1557 LogRel2(("CoreAudio: Failed to convert audio input data (%RI32:%c%c%c%c)\n", err,1558 RT_BYTE4(err), RT_BYTE3(err), RT_BYTE2(err), RT_BYTE1(err)));1559 pConvCbCtx->cErrors++;1560 }1561 1562 rc = VERR_IO_GEN_FAILURE;1563 break;1564 }1565 1566 # ifdef DEBUG_DUMP_PCM_DATA1567 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-recording-cb-conv-dst.pcm",1568 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);1569 if (RT_SUCCESS(rc))1570 {1571 RTFileWrite(fh, pvDst, cbDst, NULL);1572 RTFileClose(fh);1573 }1574 else1575 AssertFailed();1576 # endif1577 }1578 else /* Invoke FillComplexBuffer because the sample rate is different. */1579 {1580 pConvCbCtx->uPacketCnt = pSrcBuf->mDataByteSize / pSrcASBD->mBytesPerPacket;1581 pConvCbCtx->uPacketIdx = 0;1582 1583 Log3Func(("cFrames=%RU32 (%RU32 dest frames per packet) -> %RU32 input frames\n",1584 cFrames, pDstASBD->mFramesPerPacket, pConvCbCtx->uPacketCnt));1585 1586 UInt32 cPacketsToWrite = pDstBuf->mDataByteSize / pDstASBD->mBytesPerPacket;1587 Assert(cPacketsToWrite);1588 1589 UInt32 cPacketsWritten = 0;1590 1591 Log3Func(("cPacketsToWrite=%RU32\n", cPacketsToWrite));1592 1593 while (cPacketsToWrite)1594 {1595 UInt32 cPacketsIO = cPacketsToWrite;1596 1597 Log3Func(("cPacketsIO=%RU32 (In)\n", cPacketsIO));1598 1599 err = AudioConverterFillComplexBuffer(pStreamIn->ConverterRef,1600 coreAudioConverterCb, pConvCbCtx /* pvData */,1601 &cPacketsIO, &dstBufLst, NULL);1602 if (err != noErr)1603 {1604 if (pConvCbCtx->cErrors < 32) /** @todo Make this configurable. */1605 {1606 LogRel2(("CoreAudio: Failed to convert complex audio data (%RI32:%c%c%c%c)\n", err,1607 RT_BYTE4(err), RT_BYTE3(err), RT_BYTE2(err), RT_BYTE1(err)));1608 pConvCbCtx->cErrors++;1609 }1610 1611 rc = VERR_IO_GEN_FAILURE;1612 break;1613 }1614 1615 Log3Func(("cPacketsIO=%RU32 (Out)\n", cPacketsIO));1616 1617 cPacketsWritten = cPacketsIO;1618 1619 Assert(cPacketsToWrite >= cPacketsWritten);1620 cPacketsToWrite -= cPacketsWritten;1621 1622 size_t cbPacketsWritten = cPacketsWritten * pDstASBD->mBytesPerPacket;1623 Log3Func(("%RU32 packets written (%zu bytes), err=%RI32\n", cPacketsWritten, cbPacketsWritten, err));1624 1625 if (!cPacketsWritten)1626 break;1627 1628 # ifdef DEBUG_DUMP_PCM_DATA1629 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-recording-cb-complex-dst.pcm",1630 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);1631 if (RT_SUCCESS(rc))1632 {1633 RTFileWrite(fh, pvDst, cbDst, NULL);1634 RTFileClose(fh);1635 }1636 else1637 AssertFailed();1638 # endif1639 size_t cbFree = RTCircBufFree(pStreamIn->pCircBuf);1640 if (cbFree < cbDst)1641 {1642 LogRel2(("CoreAudio: Recording is lagging behind (%zu bytes available but only %zu bytes free)\n",1643 cbDst, cbFree));1644 break;1645 }1646 1647 size_t cbDstChunk;1648 void *puDst;1649 RTCircBufAcquireWriteBlock(pStreamIn->pCircBuf, cbDst, (void **)&puDst, &cbDstChunk);1650 1651 if (cbDstChunk)1652 memcpy(puDst, pvDst, cbDstChunk);1653 1654 RTCircBufReleaseWriteBlock(pStreamIn->pCircBuf, cbDstChunk);1655 }1656 }1657 1658 if (pvDst)1659 {1660 RTMemFree(pvDst);1661 pvDst = NULL;1662 }1663 }1664 else /* No converter being used. */1665 {1666 #endif /* VBOX_WITH_AUDIO_CA_CONVERTER */1667 1668 AudioStreamBasicDescription *pStreamFmt = &pStream->Unit.streamFmt;1669 1670 AssertBreakStmt(pStreamFmt->mChannelsPerFrame >= 1, rc = VERR_INVALID_PARAMETER);1671 AssertBreakStmt(pStreamFmt->mBytesPerFrame >= 1, rc = VERR_INVALID_PARAMETER);1672 1673 srcBufLst.mNumberBuffers = 1;1674 1675 AudioBuffer *pSrcBuf = &srcBufLst.mBuffers[0];1676 1677 pSrcBuf->mNumberChannels = pStreamFmt->mChannelsPerFrame;1678 pSrcBuf->mDataByteSize = pStreamFmt->mBytesPerFrame * cFrames;1679 pSrcBuf->mData = RTMemAlloc(pSrcBuf->mDataByteSize);1680 if (!pSrcBuf->mData)1681 {1682 rc = VERR_NO_MEMORY;1683 break;1684 }1685 1686 err = AudioUnitRender(pStream->Unit.audioUnit, pActionFlags, pAudioTS, 1 /* Input bus */, cFrames, &srcBufLst);1687 if (err != noErr)1688 {1689 LogRel2(("CoreAudio: Failed rendering non-converted audio input data (%RI32)\n", err));1690 rc = VERR_IO_GEN_FAILURE; /** @todo Improve this. */1691 break;1692 }1693 1694 #ifdef DEBUG_DUMP_PCM_DATA1695 RTFILE fh;1696 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-recording-cb-src.pcm",1697 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);1698 if (RT_SUCCESS(rc))1699 {1700 RTFileWrite(fh, pSrcBuf->mData, pSrcBuf->mDataByteSize, NULL);1701 RTFileClose(fh);1702 }1703 else1704 AssertFailed();1705 #endif1706 PRTCIRCBUF pCircBuf = pStream->pCircBuf;1707 1708 const uint32_t cbDataSize = pSrcBuf->mDataByteSize;1709 const size_t cbBufFree = RTCircBufFree(pCircBuf);1710 size_t cbAvail = RT_MIN(cbDataSize, cbBufFree);1711 1712 Log3Func(("cbDataSize=%RU32, cbBufFree=%zu, cbAvail=%zu\n", cbDataSize, cbBufFree, cbAvail));1713 1714 /* Iterate as long as data is available. */1715 uint8_t *puDst = NULL;1716 uint32_t cbWrittenTotal = 0;1717 while (cbAvail)1718 {1719 /* Try to acquire the necessary space from the ring buffer. */1720 size_t cbToWrite = 0;1721 RTCircBufAcquireWriteBlock(pCircBuf, cbAvail, (void **)&puDst, &cbToWrite);1722 if (!cbToWrite)1723 break;1724 1725 /* Copy the data from the Core Audio buffer to the ring buffer. */1726 memcpy(puDst, (uint8_t *)pSrcBuf->mData + cbWrittenTotal, cbToWrite);1727 1728 #ifdef DEBUG_DUMP_PCM_DATA1729 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-recording-cb-dst.pcm",1730 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);1731 if (RT_SUCCESS(rc))1732 {1733 RTFileWrite(fh, (uint8_t *)pSrcBuf->mData + cbWrittenTotal, cbToWrite, NULL);1734 RTFileClose(fh);1735 }1736 else1737 AssertFailed();1738 #endif1739 /* Release the ring buffer, so the main thread could start reading this data. */1740 RTCircBufReleaseWriteBlock(pCircBuf, cbToWrite);1741 1742 cbWrittenTotal += cbToWrite;1743 1744 Assert(cbAvail >= cbToWrite);1745 cbAvail -= cbToWrite;1746 }1747 1748 Log3Func(("cbWrittenTotal=%RU32, cbLeft=%zu\n", cbWrittenTotal, cbAvail));1749 1750 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER1751 }1752 #endif1753 1754 } while (0);1755 1756 for (UInt32 i = 0; i < srcBufLst.mNumberBuffers; i++)1757 {1758 if (srcBufLst.mBuffers[i].mData)1759 {1760 RTMemFree(srcBufLst.mBuffers[i].mData);1761 srcBufLst.mBuffers[i].mData = NULL;1762 }1763 }1764 1765 return err;1766 }1767 #endif /* !VBOX_WITH_AUDIO_CA_QUEUES */1768 1276 1769 1277 /** … … 1800 1308 break; 1801 1309 1802 #ifdef VBOX_WITH_AUDIO_CA_QUEUES1803 1310 /** 1804 1311 * Thread for a Core Audio stream's audio queue handling. … … 2161 1668 LogFunc(("pCAStream=%p\n", pCAStream)); 2162 1669 2163 int rc;2164 2165 1670 if (pCAStream->hThread != NIL_RTTHREAD) 2166 1671 { … … 2170 1675 2171 1676 int rcThread; 2172 rc = RTThreadWait(pCAStream->hThread, 30 * 1000, &rcThread);1677 int rc = RTThreadWait(pCAStream->hThread, 30 * 1000, &rcThread); 2173 1678 if (RT_FAILURE(rc)) 2174 1679 return rc; … … 2203 1708 return rc; 2204 1709 } 2205 #else /* !VBOX_WITH_AUDIO_CA_QUEUES */2206 /** @todo Eventually split up this function, as this already is huge! */2207 static int coreAudioStreamInitIn(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)2208 {2209 int rc = VINF_SUCCESS;2210 2211 UInt32 cSamples = 0;2212 2213 OSStatus err = noErr;2214 2215 LogFunc(("pCAStream=%p, pCfgReq=%p, pCfgAcq=%p\n", pCAStream, pCfgReq, pCfgAcq));2216 2217 PPDMAUDIODEVICE pDev = pCAStream->Unit.pDevice;2218 AssertPtr(pDev);2219 2220 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData;2221 AssertPtr(pData);2222 2223 AudioDeviceID deviceID = pData->deviceID;2224 LogFunc(("deviceID=%RU32\n", deviceID));2225 Assert(deviceID != kAudioDeviceUnknown);2226 2227 do2228 {2229 /* Get the default frames buffer size, so that we can setup our internal buffers. */2230 UInt32 cFrames;2231 UInt32 uSize = sizeof(cFrames);2232 2233 AudioObjectPropertyAddress propAdr;2234 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;2235 propAdr.mScope = kAudioDevicePropertyScopeInput;2236 propAdr.mElement = kAudioObjectPropertyElementMaster;2237 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &uSize, &cFrames);2238 if (err != noErr)2239 {2240 /* Can happen if no recording device is available by default. Happens on some Macs,2241 * so don't log this by default to not scare people. */2242 LogRel2(("CoreAudio: Failed to determine frame buffer size of the audio recording device (%RI32)\n", err));2243 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2244 }2245 2246 /* Set the frame buffer size and honor any minimum/maximum restrictions on the device. */2247 err = coreAudioSetFrameBufferSize(deviceID, true /* fInput */, cFrames, &cFrames);2248 if (err != noErr)2249 {2250 LogRel(("CoreAudio: Failed to set frame buffer size for the audio recording device (%RI32)\n", err));2251 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2252 }2253 2254 LogFlowFunc(("cFrames=%RU32\n", cFrames));2255 2256 /* Try to find the default HAL output component. */2257 AudioComponentDescription cd;2258 2259 RT_ZERO(cd);2260 cd.componentType = kAudioUnitType_Output;2261 cd.componentSubType = kAudioUnitSubType_HALOutput;2262 cd.componentManufacturer = kAudioUnitManufacturer_Apple;2263 2264 AudioComponent cp = AudioComponentFindNext(NULL, &cd);2265 if (cp == 0)2266 {2267 LogRel(("CoreAudio: Failed to find HAL output component\n")); /** @todo Return error value? */2268 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2269 }2270 2271 /* Open the default HAL output component. */2272 err = AudioComponentInstanceNew(cp, &pCAStream->Unit.audioUnit);2273 if (err != noErr)2274 {2275 LogRel(("CoreAudio: Failed to open output component (%RI32)\n", err));2276 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2277 }2278 2279 /* Switch the I/O mode for input to on. */2280 UInt32 uFlag = 1;2281 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input,2282 1, &uFlag, sizeof(uFlag));2283 if (err != noErr)2284 {2285 LogRel(("CoreAudio: Failed to disable input I/O mode for input stream (%RI32)\n", err));2286 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2287 }2288 2289 /* Switch the I/O mode for output to off. This is important, as this is a pure input stream. */2290 uFlag = 0;2291 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,2292 0, &uFlag, sizeof(uFlag));2293 if (err != noErr)2294 {2295 LogRel(("CoreAudio: Failed to disable output I/O mode for input stream (%RI32)\n", err));2296 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2297 }2298 2299 /* Set the default audio recording device as the device for the new AudioUnit. */2300 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,2301 0, &deviceID, sizeof(deviceID));2302 if (err != noErr)2303 {2304 LogRel(("CoreAudio: Failed to set current input device (%RI32)\n", err));2305 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2306 }2307 2308 /*2309 * CoreAudio will inform us on a second thread for new incoming audio data.2310 * Therefore register a callback function which will process the new data.2311 */2312 AURenderCallbackStruct cb;2313 RT_ZERO(cb);2314 cb.inputProc = coreAudioCaptureCb;2315 cb.inputProcRefCon = pCAStream;2316 2317 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global,2318 0, &cb, sizeof(cb));2319 if (err != noErr)2320 {2321 LogRel(("CoreAudio: Failed to register input callback (%RI32)\n", err));2322 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2323 }2324 2325 /* Create the recording device's out format based on our required audio settings. */2326 AudioStreamBasicDescription reqFmt;2327 rc = coreAudioStreamCfgToASBD(pCfgReq, &reqFmt);2328 if (RT_FAILURE(rc))2329 {2330 LogRel(("CoreAudio: Failed to convert requested input format to native format (%Rrc)\n", rc));2331 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2332 }2333 2334 coreAudioPrintASBD("Requested stream input format", &reqFmt);2335 2336 /* Fetch the input format of the recording device. */2337 AudioStreamBasicDescription devInFmt;2338 RT_ZERO(devInFmt);2339 uSize = sizeof(devInFmt);2340 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,2341 1, &devInFmt, &uSize);2342 if (err != noErr)2343 {2344 LogRel(("CoreAudio: Failed to get input device input format (%RI32)\n", err));2345 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2346 }2347 2348 coreAudioPrintASBD("Input device in (initial)", &devInFmt);2349 2350 /* Fetch the output format of the recording device. */2351 AudioStreamBasicDescription devOutFmt;2352 RT_ZERO(devOutFmt);2353 uSize = sizeof(devOutFmt);2354 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,2355 1, &devOutFmt, &uSize);2356 if (err != noErr)2357 {2358 LogRel(("CoreAudio: Failed to get input device output format (%RI32)\n", err));2359 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2360 }2361 2362 coreAudioPrintASBD("Input device out (initial)", &devOutFmt);2363 2364 /* Set the output format for the input device so that it matches the initial input format.2365 * This apparently is needed for some picky / buggy USB headsets. */2366 2367 /*2368 * The only thing we tweak here is the actual format flags: A lot of USB headsets tend2369 * to have float PCM data, which we can't handle (yet).2370 *2371 * So set this as signed integers in a packed, non-iterleaved format.2372 */2373 devInFmt.mFormatFlags = kAudioFormatFlagIsSignedInteger2374 | kAudioFormatFlagIsPacked;2375 2376 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,2377 1, &devInFmt, sizeof(devInFmt));2378 if (err != noErr)2379 {2380 LogRel(("CoreAudio: Failed to set new output format for input device (%RI32)\n", err));2381 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2382 }2383 2384 /*2385 * Also set the frame buffer size of the device on our AudioUnit. This2386 * should make sure that the frames count which we receive in the render2387 * thread is as we like.2388 */2389 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,2390 1, &cFrames, sizeof(cFrames));2391 if (err != noErr)2392 {2393 LogRel(("CoreAudio: Failed to set maximum frame buffer size for input stream (%RI32)\n", err));2394 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2395 }2396 2397 /*2398 * Initialize the new AudioUnit.2399 */2400 err = AudioUnitInitialize(pCAStream->Unit.audioUnit);2401 if (err != noErr)2402 {2403 LogRel(("CoreAudio: Failed to initialize input audio device (%RI32)\n", err));2404 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2405 }2406 2407 /* Get final input format afer initialization. */2408 uSize = sizeof(devInFmt);2409 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,2410 1, &devInFmt, &uSize);2411 if (err != noErr)2412 {2413 LogRel(("CoreAudio: Failed to re-getting input device input format (%RI32)\n", err));2414 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2415 }2416 2417 /* Print the device/stream formats again after the audio unit has been initialized.2418 * Note that the formats could have changed now. */2419 coreAudioPrintASBD("Input device in (after initialization)", &devInFmt);2420 2421 /* Get final output format afer initialization. */2422 uSize = sizeof(devOutFmt);2423 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,2424 1, &devOutFmt, &uSize);2425 if (err != noErr)2426 {2427 LogRel(("CoreAudio: Failed to re-getting input device output format (%RI32)\n", err));2428 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2429 }2430 2431 /* Print the device/stream formats again after the audio unit has been initialized.2432 * Note that the formats could have changed now. */2433 coreAudioPrintASBD("Input device out (after initialization)", &devOutFmt);2434 2435 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER2436 2437 /* If the frequency of the device' output format different from the requested one we2438 * need a converter. The same counts if the number of channels or the bits per channel are different. */2439 if ( devOutFmt.mChannelsPerFrame != reqFmt.mChannelsPerFrame2440 || devOutFmt.mSampleRate != reqFmt.mSampleRate)2441 {2442 LogRel2(("CoreAudio: Input converter is active\n"));2443 2444 err = AudioConverterNew(&devOutFmt /* Input */, &reqFmt /* Output */, &pCAStream->In.ConverterRef);2445 if (RT_UNLIKELY(err != noErr))2446 {2447 LogRel(("CoreAudio: Failed to create the audio converter (%RI32)\n", err));2448 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2449 }2450 2451 if ( devOutFmt.mChannelsPerFrame == 1 /* Mono */2452 && reqFmt.mChannelsPerFrame == 2 /* Stereo */)2453 {2454 LogRel2(("CoreAudio: Mono to stereo conversion active\n"));2455 2456 /*2457 * If the channel count is different we have to tell this the converter2458 * and supply a channel mapping. For now we only support mapping2459 * from mono to stereo. For all other cases the core audio defaults2460 * are used, which means dropping additional channels in most2461 * cases.2462 */2463 const SInt32 channelMap[2] = {0, 0}; /* Channel map for mono -> stereo. */2464 2465 err = AudioConverterSetProperty(pCAStream->In.ConverterRef, kAudioConverterChannelMap, sizeof(channelMap), channelMap);2466 if (err != noErr)2467 {2468 LogRel(("CoreAudio: Failed to set channel mapping (mono -> stereo) for the audio input converter (%RI32)\n", err));2469 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2470 }2471 }2472 2473 /* Set sample rate converter quality to maximum. */2474 uFlag = kAudioConverterQuality_Max;2475 err = AudioConverterSetProperty(pCAStream->In.ConverterRef, kAudioConverterSampleRateConverterQuality,2476 sizeof(uFlag), &uFlag);2477 if (err != noErr)2478 LogRel2(("CoreAudio: Failed to set input audio converter quality to the maximum (%RI32)\n", err));2479 2480 uSize = sizeof(UInt32);2481 UInt32 maxOutputSize;2482 err = AudioConverterGetProperty(pStreamIn->ConverterRef, kAudioConverterPropertyMaximumOutputPacketSize,2483 &uSize, &maxOutputSize);2484 if (RT_UNLIKELY(err != noErr))2485 {2486 LogRel(("CoreAudio: Failed to retrieve converter's maximum output size (%RI32)\n", err));2487 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2488 }2489 2490 LogFunc(("Maximum converter packet output size is: %RI32\n", maxOutputSize));2491 }2492 #endif /* VBOX_WITH_AUDIO_CA_CONVERTER */2493 2494 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER2495 if (pCAStream->In.ConverterRef)2496 {2497 /* Save the requested format as our stream format. */2498 memcpy(&pCAStream->Unit.streamFmt, &reqFmt, sizeof(AudioStreamBasicDescription));2499 }2500 else2501 {2502 #endif /* VBOX_WITH_AUDIO_CA_CONVERTER */2503 2504 /* Save the final output format as our stream format. */2505 memcpy(&pCAStream->Unit.streamFmt, &devOutFmt, sizeof(AudioStreamBasicDescription));2506 2507 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER2508 }2509 #endif2510 /*2511 * There are buggy devices (e.g. my Bluetooth headset) which doesn't honor2512 * the frame buffer size set in the previous calls. So finally get the2513 * frame buffer size after the AudioUnit was initialized.2514 */2515 uSize = sizeof(cFrames);2516 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,2517 0, &cFrames, &uSize);2518 if (err != noErr)2519 {2520 LogRel(("CoreAudio: Failed to get maximum frame buffer size from input audio device (%RI32)\n", err));2521 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2522 }2523 2524 /* Calculate the ratio between the device and the stream sample rate. */2525 pCAStream->In.sampleRatio = devOutFmt.mSampleRate / devInFmt.mSampleRate;2526 2527 /*2528 * Make sure that the ring buffer is big enough to hold the recording2529 * data. Compare the maximum frames per slice value with the frames2530 * necessary when using the converter where the sample rate could differ.2531 * The result is always multiplied by the channels per frame to get the2532 * samples count.2533 */2534 cSamples = RT_MAX(cFrames,2535 (cFrames * reqFmt.mBytesPerFrame * pCAStream->In.sampleRatio)2536 / reqFmt.mBytesPerFrame)2537 * reqFmt.mChannelsPerFrame;2538 if (!cSamples)2539 {2540 LogRel(("CoreAudio: Failed to determine samples buffer count input stream\n"));2541 CA_BREAK_STMT(rc = VERR_INVALID_PARAMETER);2542 }2543 2544 rc = RTCircBufCreate(&pCAStream->pCircBuf, cSamples << 1 /*pHstStrmIn->Props.cShift*/); /** @todo FIX THIS !!! */2545 if (RT_FAILURE(rc))2546 break;2547 2548 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER2549 /* Init the converter callback context. */2550 2551 /* As source, use the input device' output format,2552 * as destination, use the initially requested format. */2553 rc = coreAudioInitConvCbCtx(&pCAStream->In.convCbCtx, pCAStream,2554 &devOutFmt /* Source */, &reqFmt /* Dest */);2555 #endif2556 2557 } while (0);2558 2559 if (RT_SUCCESS(rc))2560 {2561 pCAStream->enmDir = PDMAUDIODIR_IN;2562 2563 rc = coreAudioASBDToStreamCfg(&pCAStream->Unit.streamFmt, pCfgAcq);2564 AssertRC(rc);2565 2566 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_INIT);2567 2568 pCfgAcq->cSampleBufferSize = cSamples;2569 }2570 2571 LogFunc(("cSamples=%RU32, rc=%Rrc\n", cSamples, rc));2572 return rc;2573 }2574 2575 /** @todo Eventually split up this function, as this already is huge! */2576 static int coreAudioStreamInitOut(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)2577 {2578 int rc = VINF_SUCCESS;2579 UInt32 cSamples = 0;2580 2581 OSStatus err = noErr;2582 2583 LogFunc(("pCAStream=%p, pCfgReq=%p, pCfgAcq=%p\n", pCAStream, pCfgReq, pCfgAcq));2584 2585 PPDMAUDIODEVICE pDev = pCAStream->Unit.pDevice;2586 AssertPtr(pDev);2587 2588 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData;2589 AssertPtr(pData);2590 2591 AudioDeviceID deviceID = pData->deviceID;2592 LogFunc(("deviceID=%RU32\n", deviceID));2593 Assert(deviceID != kAudioDeviceUnknown);2594 2595 do2596 {2597 /* Get the default frames buffer size, so that we can setup our internal buffers. */2598 UInt32 cFrames;2599 UInt32 uSize = sizeof(cFrames);2600 2601 AudioObjectPropertyAddress propAdr;2602 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;2603 propAdr.mScope = kAudioDevicePropertyScopeInput;2604 propAdr.mElement = kAudioObjectPropertyElementMaster;2605 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &uSize, &cFrames);2606 if (err != noErr)2607 {2608 LogRel(("CoreAudio: Failed to determine frame buffer size of the audio playback device (%RI32)\n", err));2609 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2610 }2611 2612 /* Set the frame buffer size and honor any minimum/maximum restrictions on the device. */2613 err = coreAudioSetFrameBufferSize(deviceID, false /* fInput */, cFrames, &cFrames);2614 if (err != noErr)2615 {2616 LogRel(("CoreAudio: Failed to set frame buffer size for the audio playback device (%RI32)\n", err));2617 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2618 }2619 2620 /* Try to find the default HAL output component. */2621 AudioComponentDescription cd;2622 RT_ZERO(cd);2623 2624 cd.componentType = kAudioUnitType_Output;2625 cd.componentSubType = kAudioUnitSubType_HALOutput;2626 cd.componentManufacturer = kAudioUnitManufacturer_Apple;2627 2628 AudioComponent cp = AudioComponentFindNext(NULL, &cd);2629 if (cp == 0)2630 {2631 LogRel(("CoreAudio: Failed to find HAL output component\n")); /** @todo Return error value? */2632 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2633 }2634 2635 /* Open the default HAL output component. */2636 err = AudioComponentInstanceNew(cp, &pCAStream->Unit.audioUnit);2637 if (err != noErr)2638 {2639 LogRel(("CoreAudio: Failed to open output component (%RI32)\n", err));2640 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2641 }2642 2643 /* Switch the I/O mode for output to on. */2644 UInt32 uFlag = 1;2645 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,2646 0, &uFlag, sizeof(uFlag));2647 if (err != noErr)2648 {2649 LogRel(("CoreAudio: Failed to disable I/O mode for output stream (%RI32)\n", err));2650 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2651 }2652 2653 /* Set the default audio playback device as the device for the new AudioUnit. */2654 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,2655 0, &deviceID, sizeof(deviceID));2656 if (err != noErr)2657 {2658 LogRel(("CoreAudio: Failed to set current device for output stream (%RI32)\n", err));2659 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2660 }2661 2662 /*2663 * CoreAudio will inform us on a second thread for new incoming audio data.2664 * Therefor register a callback function which will process the new data.2665 */2666 AURenderCallbackStruct cb;2667 RT_ZERO(cb);2668 cb.inputProc = coreAudioPlaybackCb;2669 cb.inputProcRefCon = pCAStream; /* pvUser */2670 2671 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,2672 0, &cb, sizeof(cb));2673 if (err != noErr)2674 {2675 LogRel(("CoreAudio: Failed to register playback callback (%RI32)\n", err));2676 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2677 }2678 2679 AudioStreamBasicDescription reqFmt;2680 coreAudioStreamCfgToASBD(pCfgReq, &reqFmt);2681 coreAudioPrintASBD("Requested stream output format", &reqFmt);2682 2683 /* Fetch the initial input format of the device. */2684 AudioStreamBasicDescription devInFmt;2685 uSize = sizeof(devInFmt);2686 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,2687 0, &devInFmt, &uSize);2688 if (err != noErr)2689 {2690 LogRel(("CoreAudio: Failed to get input format of playback device (%RI32)\n", err));2691 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2692 }2693 2694 coreAudioPrintASBD("Output device in (initial)", &devInFmt);2695 2696 /* Fetch the initial output format of the device. */2697 AudioStreamBasicDescription devOutFmt;2698 uSize = sizeof(devOutFmt);2699 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,2700 0, &devOutFmt, &uSize);2701 if (err != noErr)2702 {2703 LogRel(("CoreAudio: Failed to get output format of playback device (%RI32)\n", err));2704 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2705 }2706 2707 coreAudioPrintASBD("Output device out (initial)", &devOutFmt);2708 2709 /* Set the new input format for the output device. */2710 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,2711 0, &reqFmt, sizeof(reqFmt));2712 if (err != noErr)2713 {2714 LogRel(("CoreAudio: Failed to set stream format for output stream (%RI32)\n", err));2715 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2716 }2717 2718 /*2719 * Also set the frame buffer size off the device on our AudioUnit. This2720 * should make sure that the frames count which we receive in the render2721 * thread is as we like.2722 */2723 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,2724 0, &cFrames, sizeof(cFrames));2725 if (err != noErr)2726 {2727 LogRel(("CoreAudio: Failed to set maximum frame buffer size for output AudioUnit (%RI32)\n", err));2728 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2729 }2730 2731 /*2732 * Initialize the new AudioUnit.2733 */2734 err = AudioUnitInitialize(pCAStream->Unit.audioUnit);2735 if (err != noErr)2736 {2737 LogRel(("CoreAudio: Failed to initialize the output audio device (%RI32)\n", err));2738 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2739 }2740 2741 /* Fetch the final output format of the device after the audio unit has been initialized. */2742 uSize = sizeof(devInFmt);2743 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,2744 0, &devInFmt, &uSize);2745 if (err != noErr)2746 {2747 LogRel(("CoreAudio: Failed re-getting input format of output device (%RI32)\n", err));2748 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2749 }2750 2751 coreAudioPrintASBD("Output device in (after initialization)", &devInFmt);2752 2753 /* Save this final output format as our stream format. */2754 memcpy(&pCAStream->Unit.streamFmt, &devInFmt, sizeof(AudioStreamBasicDescription));2755 2756 /*2757 * There are buggy devices (e.g. my Bluetooth headset) which doesn't honor2758 * the frame buffer size set in the previous calls. So finally get the2759 * frame buffer size after the AudioUnit was initialized.2760 */2761 uSize = sizeof(cFrames);2762 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,2763 0, &cFrames, &uSize);2764 if (err != noErr)2765 {2766 LogRel(("CoreAudio: Failed to get maximum frame buffer size from output audio device (%RI32)\n", err));2767 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);2768 }2769 2770 /*2771 * Make sure that the ring buffer is big enough to hold the recording2772 * data. Compare the maximum frames per slice value with the frames2773 * necessary when using the converter where the sample rate could differ.2774 * The result is always multiplied by the channels per frame to get the2775 * samples count.2776 */2777 cSamples = cFrames * reqFmt.mChannelsPerFrame;2778 if (!cSamples)2779 {2780 LogRel(("CoreAudio: Failed to determine samples buffer count output stream\n"));2781 CA_BREAK_STMT(rc = VERR_INVALID_PARAMETER);2782 }2783 2784 /* Create the internal ring buffer. */2785 rc = RTCircBufCreate(&pCAStream->pCircBuf, cSamples << 1 /*pHstStrmOut->Props.cShift*/); /** @todo FIX THIS !!! */2786 2787 } while (0);2788 2789 if (RT_SUCCESS(rc))2790 {2791 pCAStream->enmDir = PDMAUDIODIR_OUT;2792 2793 rc = coreAudioASBDToStreamCfg(&pCAStream->Unit.streamFmt, pCfgAcq);2794 AssertRC(rc);2795 2796 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_INIT);2797 2798 pCfgAcq->cSampleBufferSize = cSamples;2799 }2800 2801 LogFunc(("cSamples=%RU32, rc=%Rrc\n", cSamples, rc));2802 return rc;2803 }2804 2805 /**2806 * Uninitializes a Core Audio stream.2807 *2808 * @return IPRT status code.2809 * @param pCAStream Stream to uninitialize.2810 */2811 static int coreAudioStreamUninit(PCOREAUDIOSTREAM pCAStream)2812 {2813 OSStatus err = noErr;2814 2815 if (pCAStream->Unit.audioUnit)2816 {2817 err = AudioUnitUninitialize(pCAStream->Unit.audioUnit);2818 if (err == noErr)2819 {2820 err = AudioComponentInstanceDispose(pCAStream->Unit.audioUnit);2821 if (err == noErr)2822 pCAStream->Unit.audioUnit = NULL;2823 }2824 }2825 2826 if (err == noErr)2827 {2828 if (pCAStream->pCircBuf)2829 {2830 RTCircBufDestroy(pCAStream->pCircBuf);2831 pCAStream->pCircBuf = NULL;2832 }2833 2834 pCAStream->enmStatus = COREAUDIOSTATUS_UNINIT;2835 2836 pCAStream->enmDir = PDMAUDIODIR_UNKNOWN;2837 pCAStream->pDrv = NULL;2838 2839 pCAStream->Unit.pDevice = NULL;2840 RT_ZERO(pCAStream->Unit.streamFmt);2841 2842 if (pCAStream->enmDir == PDMAUDIODIR_IN)2843 {2844 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER2845 if (pCAStream->In.ConverterRef)2846 {2847 AudioConverterDispose(pCAStream->In.ConverterRef);2848 pCAStream->In.ConverterRef = NULL;2849 }2850 2851 drvHostCoreAudioUninitConvCbCtx(&pCAStream->In.convCbCtx);2852 #endif2853 pCAStream->In.sampleRatio = 1;2854 }2855 else if (pCAStream->enmDir == PDMAUDIODIR_OUT)2856 {2857 2858 }2859 }2860 else2861 LogRel(("CoreAudio: Failed to uninit stream (%RI32)\n", err));2862 2863 return err == noErr ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Fudge! */2864 }2865 #endif /* !VBOX_WITH_AUDIO_CA_QUEUES */2866 1710 2867 1711 /** … … 3090 1934 } 3091 1935 3092 #ifndef VBOX_WITH_AUDIO_CA_QUEUES3093 /* Callback to feed audio output buffer. */3094 static DECLCALLBACK(OSStatus) coreAudioPlaybackCb(void *pvUser,3095 AudioUnitRenderActionFlags *pActionFlags,3096 const AudioTimeStamp *pAudioTS,3097 UInt32 uBusID,3098 UInt32 cFrames,3099 AudioBufferList *pBufData)3100 {3101 RT_NOREF(pActionFlags, pAudioTS, uBusID, cFrames);3102 3103 PCOREAUDIOSTREAM pStream = (PCOREAUDIOSTREAM)pvUser;3104 3105 /* Sanity. */3106 AssertPtr(pStream);3107 AssertPtr(pStream->pDrv);3108 Assert (pStream->enmDir == PDMAUDIODIR_OUT);3109 AssertPtr(pStream->Unit.pDevice);3110 3111 if (ASMAtomicReadU32(&pStream->enmStatus) != COREAUDIOSTATUS_INIT)3112 {3113 pBufData->mBuffers[0].mDataByteSize = 0;3114 return noErr;3115 }3116 3117 /* How much space is used in the ring buffer? */3118 size_t cbToRead = RT_MIN(RTCircBufUsed(pStream->pCircBuf), pBufData->mBuffers[0].mDataByteSize);3119 if (!cbToRead)3120 {3121 pBufData->mBuffers[0].mDataByteSize = 0;3122 return noErr;3123 }3124 3125 uint8_t *pbSrc = NULL;3126 size_t cbRead = 0;3127 3128 size_t cbLeft = cbToRead;3129 while (cbLeft)3130 {3131 /* Try to acquire the necessary block from the ring buffer. */3132 RTCircBufAcquireReadBlock(pStream->pCircBuf, cbLeft, (void **)&pbSrc, &cbToRead);3133 3134 /* Break if nothing is used anymore. */3135 if (!cbToRead)3136 break;3137 3138 /* Copy the data from our ring buffer to the core audio buffer. */3139 memcpy((uint8_t *)pBufData->mBuffers[0].mData + cbRead, pbSrc, cbToRead);3140 3141 /* Release the read buffer, so it could be used for new data. */3142 RTCircBufReleaseReadBlock(pStream->pCircBuf, cbToRead);3143 3144 /* Move offset. */3145 cbRead += cbToRead;3146 3147 /* Check if we're lagging behind. */3148 if (cbRead > pBufData->mBuffers[0].mDataByteSize)3149 {3150 LogRel2(("CoreAudio: Host output lagging behind, expect stuttering guest audio output\n"));3151 cbRead = pBufData->mBuffers[0].mDataByteSize;3152 break;3153 }3154 3155 Assert(cbToRead <= cbLeft);3156 cbLeft -= cbToRead;3157 }3158 3159 /* Write the bytes to the core audio buffer which were really written. */3160 Assert(pBufData->mBuffers[0].mDataByteSize >= cbRead);3161 pBufData->mBuffers[0].mDataByteSize = cbRead;3162 3163 Log3Func(("Read %zu / %zu bytes\n", cbRead, cbToRead));3164 3165 return noErr;3166 }3167 #endif /* !VBOX_WITH_AUDIO_CA_QUEUES */3168 3169 1936 /** 3170 1937 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamCapture} … … 3210 1977 uint32_t csWrittenTotal = 0; 3211 1978 3212 #ifdef VBOX_WITH_AUDIO_CA_QUEUES3213 1979 rc = RTCritSectEnter(&pCAStream->CritSect); 3214 1980 AssertRC(rc); 3215 #endif3216 1981 3217 1982 do … … 3269 2034 while (0); 3270 2035 3271 #ifdef VBOX_WITH_AUDIO_CA_QUEUES3272 2036 int rc2 = RTCritSectLeave(&pCAStream->CritSect); 3273 2037 AssertRC(rc2); 3274 #endif3275 2038 3276 2039 #ifdef LOG_ENABLED … … 3349 2112 int rc = VINF_SUCCESS; 3350 2113 3351 #ifdef VBOX_WITH_AUDIO_CA_QUEUES3352 2114 rc = RTCritSectEnter(&pCAStream->CritSect); 3353 2115 AssertRC(rc); 3354 #endif3355 2116 3356 2117 size_t cbToRead = RT_MIN(cbLive, RTCircBufFree(pCAStream->pCircBuf)); … … 3389 2150 } 3390 2151 3391 #ifdef VBOX_WITH_AUDIO_CA_QUEUES3392 2152 if ( RT_SUCCESS(rc) 3393 2153 && pCAStream->fRun … … 3405 2165 int rc2 = RTCritSectLeave(&pCAStream->CritSect); 3406 2166 AssertRC(rc2); 3407 #endif3408 2167 3409 2168 if (RT_SUCCESS(rc)) … … 3447 2206 case PDMAUDIOSTREAMCMD_RESUME: 3448 2207 { 3449 #ifdef VBOX_WITH_AUDIO_CA_QUEUES3450 2208 LogFunc(("Queue enable\n")); 3451 2209 if (pCAStream->enmDir == PDMAUDIODIR_IN) … … 3464 2222 ASMAtomicXchgBool(&pCAStream->fRun, true); 3465 2223 } 3466 #else3467 /* Only start the device if it is actually stopped */3468 if (!coreAudioUnitIsRunning(pCAStream))3469 {3470 OSStatus err = AudioUnitReset(pCAStream->Unit.audioUnit, kAudioUnitScope_Input, 0);3471 if (err != noErr)3472 {3473 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));3474 /* Keep going. */3475 }3476 3477 RTCircBufReset(pCAStream->pCircBuf);3478 3479 err = AudioOutputUnitStart(pCAStream->Unit.audioUnit);3480 if (err != noErr)3481 {3482 LogRel(("CoreAudio: Failed to start playback (%RI32)\n", err));3483 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */3484 }3485 }3486 #endif /* VBOX_WITH_AUDIO_CA_QUEUES */3487 2224 break; 3488 2225 } 3489 2226 3490 2227 case PDMAUDIOSTREAMCMD_DISABLE: 3491 #ifdef VBOX_WITH_AUDIO_CA_QUEUES3492 2228 { 3493 2229 LogFunc(("Queue disable\n")); … … 3497 2233 break; 3498 2234 } 3499 #endif3500 2235 case PDMAUDIOSTREAMCMD_PAUSE: 3501 2236 { 3502 #ifdef VBOX_WITH_AUDIO_CA_QUEUES3503 2237 LogFunc(("Queue pause\n")); 3504 2238 AudioQueuePause(pCAStream->audioQueue); 3505 2239 ASMAtomicXchgBool(&pCAStream->fIsRunning, false); 3506 #else3507 /* Only stop the device if it is actually running */3508 if (coreAudioUnitIsRunning(pCAStream))3509 {3510 OSStatus err = AudioOutputUnitStop(pCAStream->Unit.audioUnit);3511 if (err != noErr)3512 {3513 LogRel(("CoreAudio: Failed to stop playback (%RI32)\n", err));3514 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */3515 break;3516 }3517 3518 err = AudioUnitReset(pCAStream->Unit.audioUnit, kAudioUnitScope_Input, 0);3519 if (err != noErr)3520 {3521 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));3522 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */3523 }3524 }3525 #endif /* VBOX_WITH_AUDIO_CA_QUEUES */3526 2240 break; 3527 2241 } … … 3659 2373 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream; 3660 2374 3661 int rc; 3662 3663 #ifdef VBOX_WITH_AUDIO_CA_QUEUES 3664 rc = RTCritSectInit(&pCAStream->CritSect); 2375 int rc = RTCritSectInit(&pCAStream->CritSect); 3665 2376 if (RT_FAILURE(rc)) 3666 2377 return rc; … … 3670 2381 pCAStream->fIsRunning = false; 3671 2382 pCAStream->fShutdown = false; 3672 #endif3673 2383 3674 2384 /* Input or output device? */ … … 3692 2402 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_IN_INIT); 3693 2403 3694 #ifdef VBOX_WITH_AUDIO_CA_QUEUES3695 2404 rc = coreAudioStreamInitQueue(pCAStream, pCfgReq, pCfgAcq); 3696 2405 if (RT_SUCCESS(rc)) … … 3698 2407 pCfgAcq->cSampleBufferSize = _4K; /** @todo FIX THIS !!! */ 3699 2408 } 3700 #else3701 if (fIn)3702 rc = coreAudioStreamInitIn (pCAStream, pCfgReq, pCfgAcq);3703 else3704 rc = coreAudioStreamInitOut(pCAStream, pCfgReq, pCfgAcq);3705 #endif3706 2409 if (RT_SUCCESS(rc)) 3707 2410 { … … 3763 2466 } 3764 2467 3765 #ifdef VBOX_WITH_AUDIO_CA_QUEUES3766 2468 if (RT_SUCCESS(rc)) 3767 2469 { … … 3769 2471 RTCritSectDelete(&pCAStream->CritSect); 3770 2472 } 3771 #endif3772 2473 3773 2474 LogFunc(("rc=%Rrc\n", rc));
Note:
See TracChangeset
for help on using the changeset viewer.