this repo has no description
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