this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at vchroot 770 lines 20 kB view raw
1#include "darling-config.h" 2#include "AudioUnitALSA.h" 3#include "AudioUnitProperties.h" 4#include <CoreServices/MacErrors.h> 5#include <util/debug.h> 6#include <sstream> 7#include <stdexcept> 8#include <memory> 9#include <cstdio> 10#include <dispatch/dispatch.h> 11 12#define SAMPLE_PERIOD 4096 13 14static dispatch_queue_t g_audioQueue; 15 16AudioUnitALSA::AudioUnitALSA(int cardIndex, char* cardName) 17: m_cardIndex(cardIndex), m_cardName(cardName), m_pcmOutput(nullptr), m_pcmInput(nullptr) 18{ 19 static dispatch_once_t pred; 20 dispatch_once(&pred, ^{ 21 g_audioQueue = dispatch_queue_create("org.darlinghw.audiounit", nullptr); 22 }); 23} 24 25static void throwAlsaError(const std::string& msg, int errCode) 26{ 27 std::stringstream ss; 28 ss << msg << ": " << snd_strerror(errCode); 29 throw std::runtime_error(ss.str()); 30} 31 32static snd_pcm_format_t alsaFormatForASBD(const AudioStreamBasicDescription& asbd) 33{ 34 bool isFloat = asbd.mFormatFlags & kAudioFormatFlagIsFloat; 35 bool isSigned = asbd.mFormatFlags & kAudioFormatFlagIsSignedInteger; 36 bool isBE = asbd.mFormatFlags & kAudioFormatFlagIsBigEndian; 37 38 if (asbd.mFormatID == kAudioFormatLinearPCM) 39 { 40 if (isFloat) 41 return isBE ? SND_PCM_FORMAT_FLOAT_BE : SND_PCM_FORMAT_FLOAT_LE; 42 43 switch (asbd.mBitsPerChannel) 44 { 45 case 8: 46 if (isSigned) 47 return SND_PCM_FORMAT_S8; 48 else 49 return SND_PCM_FORMAT_U8; 50 case 16: 51 if (isSigned) 52 return isBE ? SND_PCM_FORMAT_S16_BE : SND_PCM_FORMAT_S16_LE; 53 else 54 return isBE ? SND_PCM_FORMAT_U16_BE : SND_PCM_FORMAT_U16_LE; 55 case 24: 56 if (isSigned) 57 return isBE ? SND_PCM_FORMAT_S24_BE : SND_PCM_FORMAT_S24_LE; 58 else 59 return isBE ? SND_PCM_FORMAT_U24_BE : SND_PCM_FORMAT_U24_LE; 60 case 32: 61 if (isSigned) 62 return isBE ? SND_PCM_FORMAT_S32_BE : SND_PCM_FORMAT_S32_LE; 63 else 64 return isBE ? SND_PCM_FORMAT_U32_BE : SND_PCM_FORMAT_U32_LE; 65 default: 66 throw std::runtime_error("Invalid mBitsPerChannel value"); 67 } 68 } 69 else 70 throw std::runtime_error("Unsupported mFormatID value"); 71} 72 73AudioUnitComponent* AudioUnitALSA::create(int cardIndex) 74{ 75 char* name; 76 77 if (cardIndex > 0) 78 { 79 if (snd_card_get_name(cardIndex, &name)) 80 return nullptr; 81 } 82 else 83 { 84 name = strdup("default" /*"plughw:0"*/); 85 } 86 87 return new AudioUnitALSA(cardIndex, name); 88} 89 90AudioUnitALSA::~AudioUnitALSA() 91{ 92 deinit(); 93 free(m_cardName); 94} 95 96OSStatus AudioUnitALSA::reset(AudioUnitScope inScope, AudioUnitElement inElement) 97{ 98 STUB(); 99 return unimpErr; 100} 101 102void AudioUnitALSA::initOutput() 103{ 104 snd_pcm_hw_params_t* hw_params = nullptr; 105 snd_pcm_sw_params_t* sw_params = nullptr; 106 107 try 108 { 109 // Configure ALSA based on m_configOutputPlayback 110 int err; 111 const AudioStreamBasicDescription& alsaConfig = m_config[kOutputBus].second; 112 unsigned int rate = alsaConfig.mSampleRate; 113 114 //if (memcmp(&m_config[kOutputBus].second, &m_config[kOutputBus].first, sizeof(AudioStreamBasicDescription)) != 0) 115 // throw std::runtime_error("Different input and HW config not implemented yet"); 116 117 // Let ALSA do the conversion on its own 118 m_config[kOutputBus].second = m_config[kOutputBus].first; 119 120 err = snd_pcm_open(&m_pcmOutput, m_cardName, SND_PCM_STREAM_PLAYBACK, 0); 121 if (err < 0) 122 throwAlsaError("Failed to initialize playback PCM", err); 123 124 err = snd_pcm_hw_params_malloc(&hw_params); 125 if (err < 0) 126 throwAlsaError("Failed to alloc hw params", err); 127 128 err = snd_pcm_hw_params_any(m_pcmOutput, hw_params); 129 if (err < 0) 130 throwAlsaError("Failed to init hw params", err); 131 132 if (isOutputPlanar()) 133 err = snd_pcm_hw_params_set_access(m_pcmOutput, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED); 134 else 135 err = snd_pcm_hw_params_set_access(m_pcmOutput, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); 136 137 if (err < 0) 138 throwAlsaError("Failed to set interleaved access", err); 139 140 err = snd_pcm_hw_params_set_format(m_pcmOutput, hw_params, alsaFormatForASBD(alsaConfig)); 141 if (err < 0) 142 throwAlsaError("Failed to set format", err); 143 144 err = snd_pcm_hw_params_set_rate_near(m_pcmOutput, hw_params, &rate, 0); 145 if (err < 0) 146 throwAlsaError("Failed to set sample rate", err); 147 148 LOG << "Channel count: " << int(alsaConfig.mChannelsPerFrame) << std::endl; 149 err = snd_pcm_hw_params_set_channels(m_pcmOutput, hw_params, alsaConfig.mChannelsPerFrame); 150 if (err < 0) 151 throwAlsaError("Failed to set channel count", err); 152 153 err = snd_pcm_hw_params(m_pcmOutput, hw_params); 154 if (err < 0) 155 throwAlsaError("Failed to set HW parameters", err); 156 157 snd_pcm_hw_params_free(hw_params); 158 hw_params = nullptr; 159 160 err = snd_pcm_sw_params_malloc(&sw_params); 161 if (err < 0) 162 throwAlsaError("Failed to alloc sw params", err); 163 164 err = snd_pcm_sw_params_current(m_pcmOutput, sw_params); 165 if (err < 0) 166 throwAlsaError("Failed to init sw params", err); 167 168 err = snd_pcm_sw_params_set_avail_min(m_pcmOutput, sw_params, SAMPLE_PERIOD); 169 if (err < 0) 170 throwAlsaError("snd_pcm_sw_params_set_avail_min() failed", err); 171 172 err = snd_pcm_sw_params_set_start_threshold(m_pcmOutput, sw_params, 0U); 173 if (err < 0) 174 throwAlsaError("snd_pcm_sw_params_set_start_threshold() failed", err); 175 176 err = snd_pcm_sw_params_set_tstamp_mode(m_pcmOutput, sw_params, SND_PCM_TSTAMP_ENABLE); 177 if (err < 0) 178 throwAlsaError("snd_pcm_sw_params_set_tstamp_mode() failed", err); 179 180 err = snd_pcm_sw_params(m_pcmOutput, sw_params); 181 if (err < 0) 182 throwAlsaError("Failed to set SW parameters", err); 183 184 snd_pcm_sw_params_free(sw_params); 185 sw_params = nullptr; 186 } 187 catch (...) 188 { 189 if (hw_params != nullptr) 190 snd_pcm_hw_params_free(hw_params); 191 if (sw_params != nullptr) 192 snd_pcm_sw_params_free(sw_params); 193 194 throw; 195 } 196} 197 198void AudioUnitALSA::initInput() 199{ 200 STUB(); 201 202 try 203 { 204 //if (memcmp(&m_config[kInputBus].second, &m_config[kInputBus].first, sizeof(AudioStreamBasicDescription)) != 0) 205 // throw std::runtime_error("Different input and HW config not implemented yet"); 206 207 // Let ALSA do the conversion on its own 208 m_config[kInputBus].first = m_config[kInputBus].second; 209 210 // TODO: support recording 211 } 212 catch (...) 213 { 214 throw; 215 } 216} 217 218OSStatus AudioUnitALSA::init() 219{ 220 try 221 { 222 if (m_pcmOutput || m_pcmInput) 223 return kAudioUnitErr_Initialized; 224 225 /*if (m_enableInput && snd_pcm_open(&m_pcmInput, m_cardName, SND_PCM_STREAM_CAPTURE, 0)) 226 { 227 deinit(); 228 229 LOG << "Failed to initialize capture PCM " << m_cardName << std::endl; 230 return kAudioUnitErr_FailedInitialization; 231 }*/ 232 233 if (m_enableOutput) 234 initOutput(); 235 if (m_enableInput) 236 initInput(); 237 } 238 catch (const std::exception& e) 239 { 240 ERROR() << e.what(); 241 242 deinit(); 243 return kAudioUnitErr_FailedInitialization; 244 } 245 246 return noErr; 247} 248 249OSStatus AudioUnitALSA::deinit() 250{ 251 if (m_pcmOutput) 252 { 253 snd_pcm_close(m_pcmOutput); 254 m_pcmOutput = nullptr; 255 } 256 if (m_pcmInput) 257 { 258 snd_pcm_close(m_pcmInput); 259 m_pcmInput = nullptr; 260 } 261 return noErr; 262} 263 264void AudioUnitALSA::processAudioEvent(struct pollfd origPoll, int event) 265{ 266 struct pollfd pfd = origPoll; 267 unsigned short revents; 268 int err; 269 270 TRACE1(event); 271 272 pfd.revents = event; 273 274 err = snd_pcm_poll_descriptors_revents(m_pcmOutput, &pfd, 1, &revents); 275 if (err < 0) 276 ERROR() << "snd_pcm_poll_descriptors_revents() failed: " << snd_strerror(err); 277 278 if (revents & POLLIN) 279 pushDataFromInput(); 280 281 if (revents & POLLOUT) 282 requestDataForPlayback(); 283} 284 285void AudioUnitALSA::requestDataForPlayback() 286{ 287 AudioUnitRenderActionFlags flags; 288 AudioTimeStamp ts; 289 AudioBufferList* bufs; 290 OSStatus err; 291 std::unique_ptr<uint8_t[]> data; 292 snd_pcm_uframes_t availFrames; 293 snd_htimestamp_t alsaTimestamp; 294 const AudioStreamBasicDescription& config = m_config[kOutputBus].first; 295 UInt32 cc = config.mChannelsPerFrame; 296 297 TRACE(); 298 299 memset(&ts, 0, sizeof(ts)); 300 301 if (isOutputPlanar()) 302 { 303 bufs = (AudioBufferList*) operator new(sizeof(AudioBufferList) + (cc-1)*sizeof(AudioBuffer)); 304 bufs->mNumberBuffers = cc; 305 } 306 else 307 { 308 bufs = (AudioBufferList*) operator new(sizeof(AudioBufferList)); 309 bufs->mNumberBuffers = 1; 310 } 311 312 if (m_shouldAllocateBuffer) 313 { 314 if (isOutputPlanar()) 315 { 316 UInt32 bytesPerChannel = config.mBytesPerFrame * SAMPLE_PERIOD; 317 318 data.reset(new uint8_t[cc*bytesPerChannel]); 319 320 for (UInt32 i = 0; i < cc; i++) 321 { 322 bufs->mBuffers[i].mNumberChannels = 1; 323 bufs->mBuffers[i].mDataByteSize = bytesPerChannel; 324 bufs->mBuffers[i].mData = data.get() + i*bytesPerChannel; 325 } 326 } 327 else 328 { 329 bufs->mBuffers[0].mNumberChannels = cc; 330 bufs->mBuffers[0].mDataByteSize = config.mBytesPerFrame * SAMPLE_PERIOD; 331 332 data.reset(new uint8_t[bufs->mBuffers[0].mDataByteSize]); 333 bufs->mBuffers[0].mData = data.get(); 334 } 335 } 336 else 337 { 338 if (isOutputPlanar()) 339 { 340 for (UInt32 i = 0; i < cc; i++) 341 { 342 bufs->mBuffers[i].mDataByteSize = 0; 343 bufs->mBuffers[i].mData = nullptr; 344 } 345 } 346 else 347 { 348 bufs->mBuffers[0].mDataByteSize = 0; 349 bufs->mBuffers[0].mData = nullptr; 350 } 351 } 352 353 snd_pcm_htimestamp(m_pcmOutput, &availFrames, &alsaTimestamp); 354 355 // TODO: fill in AudioTimeStamp based on alsaTimestamp 356 357 err = AudioUnitRender(m_inputUnit.sourceAudioUnit, &flags, &ts, kOutputBus, SAMPLE_PERIOD, bufs); 358 359 if (err != noErr) 360 { 361 ERROR() << "Render callback failed with error " << err; 362 363 // Fill with silence, the error may be temporary 364 UInt32 bytes = config.mBytesPerFrame * SAMPLE_PERIOD; 365 366 if (!m_shouldAllocateBuffer) 367 { 368 if (isOutputPlanar()) 369 { 370 data.reset(new uint8_t[bytes*cc]); 371 memset(data.get(), 0, bytes*cc); 372 } 373 else 374 { 375 data.reset(new uint8_t[bytes]); 376 memset(data.get(), 0, bytes); 377 } 378 } 379 380 if (isOutputPlanar()) 381 { 382 for (UInt32 i = 0; i < cc; i++) 383 { 384 bufs->mBuffers[i].mData = data.get() + bytes*i; 385 bufs->mBuffers[i].mDataByteSize = bytes; 386 } 387 } 388 else 389 { 390 bufs->mBuffers[0].mData = data.get(); 391 bufs->mBuffers[0].mDataByteSize = bytes; 392 } 393 } 394 395 m_lastRenderError = AudioUnitRender(this, &flags, &ts, kOutputBus, SAMPLE_PERIOD, bufs); 396 397 operator delete(bufs); 398} 399 400void AudioUnitALSA::pushDataFromInput() 401{ 402 AudioUnitRenderActionFlags flags; 403 AudioTimeStamp ts; 404 AudioBufferList* bufs; 405 OSStatus err; 406 std::unique_ptr<uint8_t[]> data; 407 snd_pcm_uframes_t availFrames; 408 snd_htimestamp_t alsaTimestamp; 409 const AudioStreamBasicDescription& config = m_config[kInputBus].second; 410 411 TRACE(); 412 413 memset(&ts, 0, sizeof(ts)); 414 415 if (m_shouldAllocateBuffer) 416 { 417 if (isInputPlanar()) 418 { 419 UInt32 cc = config.mChannelsPerFrame; 420 bufs = (AudioBufferList*) operator new(sizeof(AudioBufferList) + (cc-1)*sizeof(AudioBuffer)); 421 bufs->mNumberBuffers = cc; 422 } 423 else 424 { 425 bufs = (AudioBufferList*) operator new(sizeof(AudioBufferList)); 426 bufs->mNumberBuffers = 1; 427 } 428 429 if (isInputPlanar()) 430 { 431 UInt32 cc = config.mChannelsPerFrame; 432 UInt32 bytesPerChannel = config.mBytesPerFrame * SAMPLE_PERIOD; 433 434 data.reset(new uint8_t[cc*bytesPerChannel]); 435 436 for (UInt32 i = 0; i < cc; i++) 437 { 438 bufs->mBuffers[i].mNumberChannels = 1; 439 bufs->mBuffers[i].mDataByteSize = bytesPerChannel; 440 bufs->mBuffers[i].mData = data.get() + i*bytesPerChannel; 441 } 442 } 443 else 444 { 445 bufs->mBuffers[0].mNumberChannels = config.mChannelsPerFrame; 446 bufs->mBuffers[0].mDataByteSize = config.mBytesPerFrame * SAMPLE_PERIOD; 447 448 data.reset(new uint8_t[bufs->mBuffers[0].mDataByteSize]); 449 bufs->mBuffers[0].mData = data.get(); 450 } 451 452 //render(&flags, &ts, kInputBus, SAMPLE_PERIOD, bufs); 453 } 454 else 455 bufs = nullptr; 456 457 // TODO: fill in AudioTimeStamp based on alsaTimestamp 458 snd_pcm_htimestamp(m_pcmOutput, &availFrames, &alsaTimestamp); 459 460 m_outputCallback.inputProc(m_outputCallback.inputProcRefCon, &flags, &ts, kInputBus, SAMPLE_PERIOD, bufs); 461 462 operator delete(bufs); 463} 464 465OSStatus AudioUnitALSA::render(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) 466{ 467 if (inBusNumber == kOutputBus) 468 return renderOutput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); 469 else if (inBusNumber == kInputBus) 470 return renderInput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); 471 else 472 return paramErr; 473} 474 475OSStatus AudioUnitALSA::renderOutput(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) 476{ 477 if (!isOutputPlanar()) 478 return renderInterleavedOutput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); 479 else 480 return renderPlanarOutput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); 481} 482 483OSStatus AudioUnitALSA::renderInterleavedOutput(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) 484{ 485 int wr, sampleCount; 486 const AudioStreamBasicDescription& config = m_config[kOutputBus].first; 487 UInt32 framesSoFar = 0; 488 489 for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) 490 { 491 LOG << "Writing " << ioData->mBuffers[i].mDataByteSize << " bytes into sound card\n"; 492 493 sampleCount = std::min<UInt32>(ioData->mBuffers[i].mDataByteSize / config.mBytesPerFrame, inNumberFrames - framesSoFar); 494 framesSoFar += sampleCount; 495 496 if (!sampleCount) 497 break; 498 499do_write: 500 wr = snd_pcm_writei(m_pcmOutput, ioData->mBuffers[i].mData, sampleCount); 501 if (wr < 0) 502 { 503 if (wr == -EINTR || wr == -EPIPE) 504 { 505 LOG << "Recovering PCM\n"; 506 snd_pcm_recover(m_pcmOutput, wr, false); 507 goto do_write; 508 } 509 else 510 { 511 ERROR() << "snd_pcm_writei() failed: " << snd_strerror(wr); 512 return kAudioUnitErr_NoConnection; 513 } 514 } 515 else if (wr < sampleCount) 516 ERROR() << "snd_pcm_writei(): not all data written?"; 517 } 518 519 return noErr; 520} 521 522OSStatus AudioUnitALSA::renderPlanarOutput(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) 523{ 524 std::unique_ptr<void*[]> bufferPointers (new void*[ioData->mNumberBuffers]); 525 int size, sampleCount, wr; 526 const AudioStreamBasicDescription& config = m_config[kOutputBus].first; 527 528 if (ioData->mNumberBuffers != config.mChannelsPerFrame) 529 { 530 ERROR() << "Incorrect buffer count for planar audio, only " << ioData->mNumberBuffers; 531 return paramErr; 532 } 533 534 size = ioData->mBuffers[0].mDataByteSize; 535 for (UInt32 i = 1; i < ioData->mNumberBuffers; i++) 536 { 537 if (size != ioData->mBuffers[i].mDataByteSize) 538 { 539 ERROR() << "Bad buffer size in buffer " << i; 540 return paramErr; 541 } 542 } 543 544 for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) 545 bufferPointers[i] = ioData->mBuffers[i].mData; 546 547 sampleCount = size / config.mBytesPerFrame; 548 549do_write: 550 wr = snd_pcm_writen(m_pcmOutput, bufferPointers.get(), sampleCount); 551 if (wr < 0) 552 { 553 if (wr == -EINTR || wr == -EPIPE) 554 { 555 LOG << "Recovering PCM\n"; 556 snd_pcm_recover(m_pcmOutput, wr, false); 557 goto do_write; 558 } 559 else 560 { 561 ERROR() << "snd_pcm_writen() failed: " << snd_strerror(wr); 562 return kAudioUnitErr_NoConnection; 563 } 564 } 565 else if (wr < sampleCount) 566 ERROR() << "snd_pcm_writen(): not all data written?"; 567 568 return noErr; 569} 570 571OSStatus AudioUnitALSA::renderInput(AudioUnitRenderActionFlags *ioActionFlags,const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) 572{ 573 if (!m_outputCallback.inputProc) 574 return noErr; // We don't push, we should be polled 575 576 if (!isInputPlanar()) 577 return renderInterleavedInput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); 578 else 579 return renderPlanarInput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); 580} 581 582OSStatus AudioUnitALSA::renderInterleavedInput(AudioUnitRenderActionFlags *ioActionFlags,const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) 583{ 584 int wr, sampleCount; 585 const AudioStreamBasicDescription& config = m_config[kInputBus].second; 586 587 for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) 588 { 589 LOG << "Reading up to " << ioData->mBuffers[i].mDataByteSize << " bytes from sound card\n"; 590 591 sampleCount = ioData->mBuffers[i].mDataByteSize / config.mBytesPerFrame; 592 593do_write: 594 wr = snd_pcm_writei(m_pcmOutput, ioData->mBuffers[i].mData, sampleCount); 595 if (wr < 0) 596 { 597 if (wr == -EINTR || wr == -EPIPE) 598 { 599 snd_pcm_recover(m_pcmOutput, wr, false); 600 goto do_write; 601 } 602 else 603 { 604 ERROR() << "snd_pcm_writei() failed: " << snd_strerror(wr); 605 return kAudioUnitErr_NoConnection; 606 } 607 } 608 609 ioData->mBuffers[i].mDataByteSize = wr * config.mBytesPerFrame; 610 } 611 612 return noErr; 613} 614 615OSStatus AudioUnitALSA::renderPlanarInput(AudioUnitRenderActionFlags *ioActionFlags,const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) 616{ 617 std::unique_ptr<void*[]> bufferPointers (new void*[ioData->mNumberBuffers]); 618 int size, sampleCount, wr; 619 const AudioStreamBasicDescription& config = m_config[kInputBus].second; 620 621 if (ioData->mNumberBuffers != config.mChannelsPerFrame) 622 { 623 ERROR() << "Incorrect buffer count for planar audio, only " << ioData->mNumberBuffers; 624 return paramErr; 625 } 626 627 size = ioData->mBuffers[0].mDataByteSize; 628 for (UInt32 i = 1; i < ioData->mNumberBuffers; i++) 629 { 630 if (size != ioData->mBuffers[i].mDataByteSize) 631 { 632 ERROR() << "Bad buffer size in buffer " << i; 633 return paramErr; 634 } 635 } 636 637 for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) 638 bufferPointers[i] = ioData->mBuffers[i].mData; 639 640 sampleCount = size / config.mBytesPerFrame; 641 642do_write: 643 wr = snd_pcm_readn(m_pcmInput, bufferPointers.get(), sampleCount); 644 if (wr < 0) 645 { 646 if (wr == -EINTR || wr == -EPIPE) 647 { 648 snd_pcm_recover(m_pcmInput, wr, false); 649 goto do_write; 650 } 651 else 652 { 653 ERROR() << "snd_pcm_writen() failed: " << snd_strerror(wr); 654 return kAudioUnitErr_NoConnection; 655 } 656 } 657 658 for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) 659 ioData->mBuffers[i].mDataByteSize = sampleCount * config.mBytesPerFrame; 660 661 return noErr; 662} 663 664void AudioUnitALSA::startOutput() 665{ 666 std::unique_ptr<struct pollfd[]> pollfds; 667 int count, err; 668 669 if (!m_inputUnit.sourceAudioUnit) 670 throw std::runtime_error("No input unit set"); 671 672 err = snd_pcm_prepare(m_pcmOutput); 673 if (err != 0) 674 throwAlsaError("snd_pcm_prepare() failed", err); 675 676 count = snd_pcm_poll_descriptors_count(m_pcmOutput); 677 pollfds.reset(new struct pollfd[count]); 678 679 if (snd_pcm_poll_descriptors(m_pcmOutput, pollfds.get(), count) != count) 680 throw std::runtime_error("snd_pcm_poll_descriptors() failed"); 681 682 LOG << "ALSA descriptor count: " << count << std::endl; 683 startDescriptors(pollfds.get(), count); 684} 685 686void AudioUnitALSA::startDescriptors(const struct pollfd* pollfds, int count) 687{ 688 for (int i = 0; i < count; i++) 689 { 690 dispatch_source_t source; 691 const struct pollfd& cur = pollfds[i]; 692 693 if (cur.events & POLLIN) 694 { 695 source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, cur.fd, 0, g_audioQueue); 696 dispatch_source_set_event_handler(source, ^{ 697 processAudioEvent(cur, POLLIN); 698 }); 699 dispatch_resume(source); 700 m_sources.push_back(source); 701 } 702 703 if (cur.events & POLLOUT) 704 { 705 source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, cur.fd, 0, g_audioQueue); 706 dispatch_source_set_event_handler(source, ^{ 707 processAudioEvent(cur, POLLOUT); 708 }); 709 dispatch_resume(source); 710 m_sources.push_back(source); 711 } 712 713 } 714} 715 716void AudioUnitALSA::startInput() 717{ 718 int err; 719 720 STUB(); 721 722 err = snd_pcm_prepare(m_pcmInput); 723 if (err != 0) 724 throwAlsaError("snd_pcm_prepare() failed", err); 725} 726 727OSStatus AudioUnitALSA::start() 728{ 729 int err; 730 731 TRACE(); 732 733 try 734 { 735 if (m_pcmInput) 736 startInput(); 737 738 if (m_pcmOutput) 739 startOutput(); 740 741 } 742 catch (const std::exception& e) 743 { 744 if (m_pcmInput) 745 snd_pcm_drop(m_pcmInput); 746 if (m_pcmOutput) 747 snd_pcm_drop(m_pcmOutput); 748 749 ERROR() << e.what(); 750 return kAudioUnitErr_FailedInitialization; 751 } 752 753 return noErr; 754} 755 756OSStatus AudioUnitALSA::stop() 757{ 758 TRACE(); 759 760 for (dispatch_source_t src : m_sources) 761 dispatch_source_cancel(src); 762 m_sources.clear(); 763 764 if (m_pcmInput) 765 snd_pcm_drop(m_pcmInput); 766 if (m_pcmOutput) 767 snd_pcm_drop(m_pcmOutput); 768 return noErr; 769} 770