msiounit.m 31 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * auiosnd.c -I/O unit Media plugin for Linphone-
 *
 *
 * Copyright (C) 2009  Belledonne Comunications, Grenoble, France
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
21 22 23
#import <AVFoundation/AVAudioSession.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
24 25 26
#include <AudioToolbox/AudioToolbox.h>
#include "mediastreamer2/mssndcard.h"
#include "mediastreamer2/msfilter.h"
27
#include "mediastreamer2/msticker.h"
28

29 30
static const int flowControlInterval = 5000; // ms
static const int flowControlThreshold = 40; // ms
31

jehan's avatar
jehan committed
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
/*                          -------------------------
							| i                   o |
-- BUS 1 -- from mic -->	| n    REMOTE I/O     u | -- BUS 1 -- to app -->
							| p      AUDIO        t |
-- BUS 0 -- from app -->	| u       UNIT        p | -- BUS 0 -- to speaker -->
							| t                   u |
							|                     t |
							-------------------------
 */

static AudioUnitElement inputBus = 1;
static AudioUnitElement outputBus = 0;


static const char * audio_unit_format_error (OSStatus error) {
	switch (error) {
		case kAudioUnitErr_InvalidProperty: return "kAudioUnitErr_InvalidProperty";
		case kAudioUnitErr_InvalidParameter: return "kAudioUnitErr_InvalidParameter";
		case kAudioUnitErr_InvalidElement: return "kAudioUnitErr_InvalidElement";
		case kAudioUnitErr_NoConnection: return "kAudioUnitErr_NoConnection";
		case kAudioUnitErr_FailedInitialization: return "kAudioUnitErr_FailedInitialization";
		case kAudioUnitErr_TooManyFramesToProcess: return "kAudioUnitErr_TooManyFramesToProcess";
		case kAudioUnitErr_InvalidFile: return "kAudioUnitErr_InvalidFile";
		case kAudioUnitErr_UnknownFileType: return "kAudioUnitErr_UnknownFileType";
		case kAudioUnitErr_FileNotSpecified: return "kAudioUnitErr_FileNotSpecified";
		case kAudioUnitErr_FormatNotSupported: return "kAudioUnitErr_FormatNotSupported";
		case kAudioUnitErr_Uninitialized: return "kAudioUnitErr_Uninitialized";
		case kAudioUnitErr_InvalidScope: return "kAudioUnitErr_InvalidScope";
		case kAudioUnitErr_PropertyNotWritable: return "kAudioUnitErr_PropertyNotWritable";
		case kAudioUnitErr_CannotDoInCurrentContext: return "kAudioUnitErr_CannotDoInCurrentContext";
		case kAudioUnitErr_InvalidPropertyValue: return "kAudioUnitErr_InvalidPropertyValue";
		case kAudioUnitErr_PropertyNotInUse: return "kAudioUnitErr_PropertyNotInUse";
		case kAudioUnitErr_Initialized: return "kAudioUnitErr_Initialized";
		case kAudioUnitErr_InvalidOfflineRender: return "kAudioUnitErr_InvalidOfflineRender";
		case kAudioUnitErr_Unauthorized: return "kAudioUnitErr_Unauthorized";
67 68 69 70 71 72 73 74
		default: {
			ms_error ("Cannot start audioUnit because [%c%c%c%c]"
						  ,((char*)&error)[3]
						  ,((char*)&error)[2]
						  ,((char*)&error)[1]
						  ,((char*)&error)[0]);
			return "unknown error";
		} 
jehan's avatar
jehan committed
75
	}
76

jehan's avatar
jehan committed
77 78 79 80
}


#define check_au_session_result(au,method) \
81
if (au!=AVAudioSessionErrorInsufficientPriority && au!=0) ms_error("AudioSession error for %s: ret=%i (%s:%d)",method, au, __FILE__, __LINE__ )
jehan's avatar
jehan committed
82 83

#define check_au_unit_result(au,method) \
84
if (au!=0) ms_error("AudioUnit error for %s: ret=%s (%li) (%s:%d)",method, audio_unit_format_error(au), (long)au, __FILE__, __LINE__ )
85 86 87 88 89


#define check_session_call(call)   do { OSStatus res = (call); check_au_session_result(res, #call); } while(0)
#define check_audiounit_call(call) do { OSStatus res = (call); check_au_unit_result(res, #call); } while(0)

jehan's avatar
jehan committed
90

91 92 93 94
#if 0
#undef ms_debug
#define ms_debug ms_message
#endif
95
static const char* AU_CARD_RECEIVER = "Audio Unit Receiver";
96
static const char* AU_CARD_NOVOICEPROC = "Audio Unit NoVoiceProc";
jehan's avatar
jehan committed
97
static const char* AU_CARD_FAST_IOUNIT = "Audio Unit Fast Receiver"; /*Same as AU_CARD_RECEIVER but whiout audio session handling which are delagated to the application*/
98
static const char* AU_CARD_SPEAKER = "Audio Unit Speaker";
99
static const char* AU_CARD_TESTER = "Audio Unit Tester";
100

101 102 103 104

static MSFilter *ms_au_read_new(MSSndCard *card);
static MSFilter *ms_au_write_new(MSSndCard *card);

jehan's avatar
jehan committed
105 106 107 108
typedef  struct au_filter_read_data au_filter_read_data_t;
typedef  struct au_filter_write_data au_filter_write_data_t;


jehan's avatar
jehan committed
109
typedef  struct  au_card {
110
	AudioUnit	io_unit;
111
	ms_mutex_t	mutex;
112 113 114
	unsigned int	rate;
	unsigned int	bits;
	unsigned int	nchannels;
115
	uint64_t last_failed_iounit_start_time;
116 117 118 119
	au_filter_read_data_t* read_data;
	au_filter_write_data_t* write_data;
	MSSndCard* ms_snd_card;
	CFRunLoopTimerRef shutdown_timer;
jehan's avatar
jehan committed
120 121
	bool_t is_ringer;
	bool_t is_fast;
122
	bool_t is_tester;
jehan's avatar
jehan committed
123 124 125 126
	bool_t io_unit_started;
	bool_t audio_session_configured;
	bool_t read_started;
	bool_t write_started;
127
	bool_t use_shutdowntimer;
jehan's avatar
jehan committed
128 129 130 131
}au_card_t;

typedef  struct au_filter_base {
	au_card_t* card;
132
	int muted;
jehan's avatar
jehan committed
133 134
}au_filter_base_t;

jehan's avatar
jehan committed
135
struct au_filter_read_data{
jehan's avatar
jehan committed
136
	au_filter_base_t base;
137 138 139 140
	ms_mutex_t	mutex;
	queue_t		rq;
	AudioTimeStamp readTimeStamp;
	unsigned int n_lost_frame;
141 142
	MSTickerSynchronizer *ticker_synchronizer;
	uint64_t read_samples;
jehan's avatar
jehan committed
143
} ;
jehan's avatar
jehan committed
144

jehan's avatar
jehan committed
145
struct au_filter_write_data{
jehan's avatar
jehan committed
146
	au_filter_base_t base;
147 148
	ms_mutex_t mutex;
	MSFlowControlledBufferizer *bufferizer;
jehan's avatar
jehan committed
149
	unsigned int n_lost_frame;
jehan's avatar
jehan committed
150
};
jehan's avatar
jehan committed
151

jehan's avatar
jehan committed
152
static void  stop_audio_unit (au_card_t* d);
153
static void cancel_audio_unit_timer(au_card_t* card);
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172


/*
 mediastreamer2 function
 */

static void au_set_level(MSSndCard *card, MSSndCardMixerElem e, int percent)
{
}

static int au_get_level(MSSndCard *card, MSSndCardMixerElem e)
{
	return 0;
}

static void au_set_source(MSSndCard *card, MSSndCardCapture source)
{
}

jehan's avatar
jehan committed
173 174 175 176 177 178 179 180
static OSStatus au_render_cb (
							  void                        *inRefCon,
							  AudioUnitRenderActionFlags  *ioActionFlags,
							  const AudioTimeStamp        *inTimeStamp,
							  UInt32                      inBusNumber,
							  UInt32                      inNumberFrames,
							  AudioBufferList             *ioData
							  );
181

182 183 184 185 186 187 188 189 190 191
static OSStatus au_read_cb (
							  void                        *inRefCon,
							  AudioUnitRenderActionFlags  *ioActionFlags,
							  const AudioTimeStamp        *inTimeStamp,
							  UInt32                      inBusNumber,
							  UInt32                      inNumberFrames,
							  AudioBufferList             *ioData
);

static void create_io_unit (AudioUnit* au, au_card_t *card) {
192 193
	AudioComponentDescription au_description;
	AudioComponent foundComponent;
194
	MSSndCard* sndcard = card->ms_snd_card;
195

196
	bool_t noVoiceProc = (strcasecmp(sndcard->name, AU_CARD_NOVOICEPROC) == 0);
197 198
	OSType subtype = noVoiceProc ? kAudioUnitSubType_RemoteIO : kAudioUnitSubType_VoiceProcessingIO;

199
	au_description.componentType          = kAudioUnitType_Output;
200
	au_description.componentSubType       = subtype;
201 202 203
	au_description.componentManufacturer  = kAudioUnitManufacturer_Apple;
	au_description.componentFlags         = 0;
	au_description.componentFlagsMask     = 0;
204

205
	foundComponent = AudioComponentFindNext (NULL,&au_description);
206

207
	check_audiounit_call( AudioComponentInstanceNew(foundComponent, au) );
208

209 210 211 212 213 214 215 216 217 218 219 220 221
	//Always configure readcb
	AURenderCallbackStruct renderCallbackStruct;
	renderCallbackStruct.inputProc       = au_read_cb;
	renderCallbackStruct.inputProcRefCon = card;
	check_audiounit_call(AudioUnitSetProperty (
								   card->io_unit,
								   kAudioOutputUnitProperty_SetInputCallback,
								   kAudioUnitScope_Input,
								   outputBus,
								   &renderCallbackStruct,
								   sizeof (renderCallbackStruct)
								   ));

222
	ms_message("AudioUnit created with type %s.", subtype==kAudioUnitSubType_RemoteIO ? "kAudioUnitSubType_RemoteIO" : "kAudioUnitSubType_VoiceProcessingIO" );
223
}
224 225


226 227
/* the interruption listener is not reliable, it can be overriden by other parts of the application */
/* as a result, we do nothing with it*/
228
static void au_interruption_listener (void     *inClientData, UInt32   inInterruptionState){
229
/*
230 231 232 233 234
	if (inInterruptionState==kAudioSessionBeginInterruption){
		CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^(void) {
			stop_audio_unit((au_card_t*)inClientData);
		});
	}
235
*/
236 237
}

238 239
static void au_init(MSSndCard *card){
	ms_debug("au_init");
jehan's avatar
jehan committed
240 241 242 243 244 245
	au_card_t *d=ms_new0(au_card_t,1);

	if (strcmp(card->name,AU_CARD_SPEAKER)==0) {
		d->is_ringer=TRUE;
	} else if (strcmp(card->name,AU_CARD_FAST_IOUNIT)==0) {
		d->is_fast=TRUE;
246 247
	} else if( strcmp(card->name,AU_CARD_TESTER)==0){
		d->is_tester=TRUE;
jehan's avatar
jehan committed
248
	}
249
	d->bits=16;
250
	d->rate=0; /*not set*/
jehan's avatar
jehan committed
251 252
	d->nchannels=1;
	d->ms_snd_card=card;
253
	d->use_shutdowntimer=FALSE;
254
	card->preferred_sample_rate=44100;
255
	card->capabilities|=MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER|MS_SND_CARD_CAP_IS_SLOW;
256
	ms_mutex_init(&d->mutex,NULL);
257
	card->data=d;
258 259 260
}

static void au_uninit(MSSndCard *card){
261
	au_card_t *d=(au_card_t*)card->data;
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
262
	cancel_audio_unit_timer(d);
263
	stop_audio_unit(d);
264
	ms_mutex_destroy(&d->mutex);
265
	ms_free(d);
266
	card->data = NULL;
267 268
}

269 270
static void au_usage_hint(MSSndCard *card, bool_t used){
	au_card_t *d=(au_card_t*)card->data;
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
	if (!used &&d){
		if(d->io_unit) {
			if (d->shutdown_timer) {
				cancel_audio_unit_timer(d);
				stop_audio_unit(d);
			} else {
				d->use_shutdowntimer = FALSE;
			}
		} else {
			d->use_shutdowntimer = FALSE;
		}
	} else {
		if(d) {
			d->use_shutdowntimer = TRUE;
		}
286 287 288 289 290
	}
}



291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
static void au_detect(MSSndCardManager *m);
static MSSndCard *au_duplicate(MSSndCard *obj);

MSSndCardDesc au_card_desc={
.driver_type="AU",
.detect=au_detect,
.init=au_init,
.set_level=au_set_level,
.get_level=au_get_level,
.set_capture=au_set_source,
.set_control=NULL,
.get_control=NULL,
.create_reader=ms_au_read_new,
.create_writer=ms_au_write_new,
.uninit=au_uninit,
306 307
.duplicate=au_duplicate,
.usage_hint=au_usage_hint
308 309 310
};

static MSSndCard *au_duplicate(MSSndCard *obj){
311
	MSSndCard *card=ms_snd_card_new_with_name(&au_card_desc,obj->name);
312 313
	return card;
}
314

315
static MSSndCard *au_card_new(const char* name){
316
	MSSndCard *card=ms_snd_card_new_with_name(&au_card_desc,name);
317 318 319 320 321
	return card;
}

static void au_detect(MSSndCardManager *m){
	ms_debug("au_detect");
322
	MSSndCard *card=au_card_new(AU_CARD_RECEIVER);
323
	ms_snd_card_manager_add_card(m,card);
324 325 326 327 328
	card=au_card_new(AU_CARD_FAST_IOUNIT);
	ms_snd_card_manager_add_card(m,card);
	card = au_card_new(AU_CARD_NOVOICEPROC);
	ms_snd_card_manager_add_card(m,card);
	card = au_card_new(AU_CARD_TESTER);
329
	ms_snd_card_manager_add_card(m,card);
330 331
}

332
/********************write cb only used for write operation******************/
333 334 335 336 337 338 339 340 341
static OSStatus au_read_cb (
							  void                        *inRefCon,
							  AudioUnitRenderActionFlags  *ioActionFlags,
							  const AudioTimeStamp        *inTimeStamp,
							  UInt32                      inBusNumber,
							  UInt32                      inNumberFrames,
							  AudioBufferList             *ioData
)
{
jehan's avatar
jehan committed
342
	au_card_t* card = (au_card_t*)inRefCon;
343
	ms_mutex_lock(&card->mutex);
jehan's avatar
jehan committed
344
	if (!card->read_data) {
345 346
		//just return from now;
		ms_mutex_unlock(&card->mutex);
jehan's avatar
jehan committed
347 348 349
		return 0;
	}
	au_filter_read_data_t *d=card->read_data;
350 351 352 353 354
	if (d->readTimeStamp.mSampleTime <0) {
		d->readTimeStamp=*inTimeStamp;
	}
	OSStatus err=0;
	mblk_t * rm=NULL;
jehan's avatar
jehan committed
355
	AudioBufferList readAudioBufferList;
356
	readAudioBufferList.mBuffers[0].mDataByteSize=inNumberFrames*d->base.card->bits/8;
jehan's avatar
jehan committed
357 358 359 360 361 362 363
	readAudioBufferList.mNumberBuffers=1;
	readAudioBufferList.mBuffers[0].mNumberChannels=d->base.card->nchannels;

	if (d->base.card->read_started) {
		rm=allocb(readAudioBufferList.mBuffers[0].mDataByteSize,0);
		readAudioBufferList.mBuffers[0].mData=rm->b_wptr;
		err = AudioUnitRender(d->base.card->io_unit, ioActionFlags, &d->readTimeStamp, inBusNumber,inNumberFrames, &readAudioBufferList);
364
		if (err == 0) {
jehan's avatar
jehan committed
365
			rm->b_wptr += readAudioBufferList.mBuffers[0].mDataByteSize;
366 367 368
			ms_mutex_lock(&d->mutex);
			putq(&d->rq,rm);
			ms_mutex_unlock(&d->mutex);
jehan's avatar
jehan committed
369
			d->readTimeStamp.mSampleTime+=readAudioBufferList.mBuffers[0].mDataByteSize/(d->base.card->bits/2);
370
		} else {
371 372 373
			check_au_unit_result(err, "AudioUnitRender");
			freeb(rm);
		}
374
	}
375
	ms_mutex_unlock(&card->mutex);
376 377 378
	return err;
}

jehan's avatar
jehan committed
379
static OSStatus au_write_cb (
380 381 382 383 384 385
							  void                        *inRefCon,
							  AudioUnitRenderActionFlags  *ioActionFlags,
							  const AudioTimeStamp        *inTimeStamp,
							  UInt32                      inBusNumber,
							  UInt32                      inNumberFrames,
							  AudioBufferList             *ioData
jehan's avatar
jehan committed
386
							 ) {
387
	ms_debug("render cb");
jehan's avatar
jehan committed
388
	au_card_t* card = (au_card_t*)inRefCon;
389
	ms_mutex_lock(&card->mutex);
390 391 392 393
	if( !card->write_data ){
		ms_mutex_unlock(&card->mutex);
		return -1;
	}
394 395
	ioData->mBuffers[0].mDataByteSize=inNumberFrames*card->bits/8;
	ioData->mNumberBuffers=1;
396

397
	au_filter_write_data_t *d=card->write_data;
398

399
	if (d!=NULL){
400
		unsigned int size;
401
		ms_mutex_lock(&d->mutex);
402 403
		size = inNumberFrames*d->base.card->bits/8;
		if (ms_flow_controlled_bufferizer_get_avail(d->bufferizer) >= size) {
404
			ms_flow_controlled_bufferizer_read(d->bufferizer, ioData->mBuffers[0].mData, size);
405
		} else {
406 407 408 409 410
			//writing silence;
			memset(ioData->mBuffers[0].mData, 0,ioData->mBuffers[0].mDataByteSize);
			ms_debug("nothing to write, pushing silences,  framezize is %u bytes mDataByteSize %u"
					 ,inNumberFrames*card->bits/8
					 ,(unsigned int)ioData->mBuffers[0].mDataByteSize);
411
		}
412
		ms_mutex_unlock(&d->mutex);
413
	}
414
	ms_mutex_unlock(&card->mutex);
415 416 417 418
	return 0;
}

/****************config**************/
jehan's avatar
jehan committed
419
static void configure_audio_session (au_card_t* d,uint64_t time) {
420 421
	NSString *audioCategory;
	NSString *audioMode;
Benjamin REIS's avatar
Benjamin REIS committed
422
	NSError *err = nil;;
423 424
	//UInt32 audioCategorySize=sizeof(audioCategory);
	AVAudioSession *audioSession = [AVAudioSession sharedInstance];
425
	bool_t changed;
426

427
	if (!d->is_fast){
428

429
		if (d->audio_session_configured){
430
			/*check that category wasn't changed*/
431
			audioCategory = audioSession.category;
432

433 434
			changed=(audioCategory!=AVAudioSessionCategoryAmbient && d->is_ringer)
			||(audioCategory!=AVAudioSessionCategoryPlayAndRecord && !d->is_ringer);
435
		}
436

437
		if (!d->audio_session_configured || changed) {
438
			[audioSession setActive:TRUE error:&err];
Benjamin REIS's avatar
Benjamin REIS committed
439
			if(err) ms_error("Unable to activate audio session because : %s", [err localizedDescription].UTF8String);
Benjamin REIS's avatar
Benjamin REIS committed
440
			err = nil;
441

442
			if (d->is_ringer && kCFCoreFoundationVersionNumber > kCFCoreFoundationVersionNumber10_6 /*I.E is >=OS4*/) {
443
				audioCategory = AVAudioSessionCategoryAmbient;
444
				audioMode = AVAudioSessionModeDefault;
445
				ms_message("Configuring audio session for playback");
446
			} else {
447 448
				audioCategory = AVAudioSessionCategoryPlayAndRecord;
				audioMode = AVAudioSessionModeVoiceChat;
449
				ms_message("Configuring audio session for playback/record");
450
			}
451

452
			[audioSession setCategory:audioCategory error:&err];
Benjamin REIS's avatar
Benjamin REIS committed
453
			if(err) ms_error("Unable to change audio category because : %s", [err localizedDescription].UTF8String);
Benjamin REIS's avatar
Benjamin REIS committed
454
			err = nil;
455 456

			[audioSession setMode:audioMode error:&err];
Benjamin REIS's avatar
Benjamin REIS committed
457
			if(err) ms_error("Unable to change audio mode because : %s", [err localizedDescription].UTF8String);
Benjamin REIS's avatar
Benjamin REIS committed
458
			err = nil;
Simon Morlat's avatar
Simon Morlat committed
459 460
		}else{
			ms_message("Audio session already correctly configured.");
jehan's avatar
jehan committed
461 462 463
		}
		d->audio_session_configured=TRUE;
	} else {
Simon Morlat's avatar
Simon Morlat committed
464
		ms_message("Fast iounit mode, audio session configuration must be done at application level.");
465
	}
466

jehan's avatar
jehan committed
467
}
468

jehan's avatar
jehan committed
469 470
static bool_t  start_audio_unit (au_filter_base_t* d,uint64_t time) {
	au_card_t* card=d->card;
471
	AVAudioSession *audioSession = [AVAudioSession sharedInstance];
472
	if (card->io_unit == NULL) {
473
		create_io_unit(&card->io_unit, card);
474 475
		if (card->io_unit == NULL) ms_fatal("io_unit is NULL");
	}
476
	if (!card->io_unit_started && (card->last_failed_iounit_start_time == 0 || (time - card->last_failed_iounit_start_time)>100)) {
477

478
		check_audiounit_call(AudioUnitInitialize(card->io_unit));
jehan's avatar
jehan committed
479
		ms_message("io unit initialized");
480

jehan's avatar
jehan committed
481 482
		Float64 delay;
		UInt32 delaySize = sizeof(delay);
483
		check_audiounit_call(AudioUnitGetProperty(card->io_unit
jehan's avatar
jehan committed
484 485 486 487
									  ,kAudioUnitProperty_Latency
									  , kAudioUnitScope_Global
									  , 0
									  , &delay
488
									  , &delaySize));
489

jehan's avatar
jehan committed
490 491
		UInt32 quality;
		UInt32 qualitySize = sizeof(quality);
492
		check_audiounit_call(AudioUnitGetProperty(card->io_unit
jehan's avatar
jehan committed
493 494 495 496
									  ,kAudioUnitProperty_RenderQuality
									  , kAudioUnitScope_Global
									  , 0
									  , &quality
497
									  , &qualitySize));
498
		ms_message("I/O unit latency [%f], quality [%u]",delay,(unsigned)quality);
499 500 501 502 503 504 505
		Float32 hwoutputlatency = audioSession.outputLatency;

		Float32 hwinputlatency = audioSession.inputLatency;

		Float32 hwiobuf = audioSession.IOBufferDuration;

		Float64 hwsamplerate = audioSession.sampleRate;
506 507 508

		OSStatus auresult;
		check_audiounit_call( (auresult = AudioOutputUnitStart(card->io_unit)) );
jehan's avatar
jehan committed
509
		card->io_unit_started = (auresult ==0);
510
		if (!card->io_unit_started) {
511
			ms_message("AudioUnit could not be started, current hw output latency [%f] input [%f] iobuf[%f] hw sample rate [%f]",hwoutputlatency,hwinputlatency,hwiobuf,hwsamplerate);
jehan's avatar
jehan committed
512 513
			d->card->last_failed_iounit_start_time=time;
		} else {
514
			ms_message("AudioUnit started, current hw output latency [%f] input [%f] iobuf[%f] hw sample rate [%f]",hwoutputlatency,hwinputlatency,hwiobuf,hwsamplerate);
jehan's avatar
jehan committed
515 516
			d->card->last_failed_iounit_start_time=0;
		}
517
	}
jehan's avatar
jehan committed
518
	return card->io_unit_started;
519

jehan's avatar
jehan committed
520
}
521

522
static void destroy_audio_unit (au_card_t* d) {
jehan's avatar
jehan committed
523
	if (d->io_unit) {
524 525
		AudioComponentInstanceDispose (d->io_unit);
		d->io_unit=NULL;
526
		if (!d->is_fast) {
Benjamin REIS's avatar
Benjamin REIS committed
527
			NSError *err = nil;;
528
			[[AVAudioSession sharedInstance] setActive:FALSE withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&err];
Benjamin REIS's avatar
Benjamin REIS committed
529
			if(err) ms_error("Unable to activate audio session because : %s", [err localizedDescription].UTF8String);
Benjamin REIS's avatar
Benjamin REIS committed
530
			err = nil;
531
		}
Simon Morlat's avatar
Simon Morlat committed
532
		ms_message("AudioUnit destroyed");
533 534
	}
}
535
static void stop_audio_unit (au_card_t* d) {
536
	if (d->io_unit && d->io_unit_started) {
537
		check_audiounit_call( AudioOutputUnitStop(d->io_unit) );
Simon Morlat's avatar
Simon Morlat committed
538
		ms_message("AudioUnit stopped");
jehan's avatar
jehan committed
539 540
		d->io_unit_started=FALSE;
		d->audio_session_configured=FALSE;
541

542
	}
543
	if (d->io_unit) {
544
		check_audiounit_call( AudioUnitUninitialize(d->io_unit) );
jehan's avatar
jehan committed
545 546
		destroy_audio_unit(d);
	}
547
	d->rate=0; /*uninit*/
jehan's avatar
jehan committed
548 549
}

550
static void cancel_audio_unit_timer(au_card_t* card){
551 552 553 554 555 556
	if (card->shutdown_timer){
		CFRunLoopRemoveTimer(CFRunLoopGetMain(), card->shutdown_timer,kCFRunLoopCommonModes);
		CFRunLoopTimerInvalidate(card->shutdown_timer);
		card->shutdown_timer=NULL;
	}
}
jehan's avatar
jehan committed
557 558 559 560 561 562

/***********************************read function********************/

static void au_read_preprocess(MSFilter *f){
	au_filter_read_data_t *d= (au_filter_read_data_t*)f->data;
	au_card_t* card=d->base.card;
563
	AVAudioSession *audioSession = [AVAudioSession sharedInstance];
Benjamin REIS's avatar
Benjamin REIS committed
564
	NSError *err = nil;;
565
	cancel_audio_unit_timer(card);
jehan's avatar
jehan committed
566
	configure_audio_session(card, f->ticker->time);
567

568
	if (!card->io_unit) create_io_unit(&card->io_unit, card);
569
	d->ticker_synchronizer = ms_ticker_synchronizer_new();
570
	ms_ticker_set_synchronizer(f->ticker, d->ticker_synchronizer);
571

jehan's avatar
jehan committed
572 573 574 575
	if (card->io_unit_started) {
		ms_message("Audio Unit already started");
		return;
	}
576

577 578 579 580
	/*format are always set in the write preprocess*/
	Float32 preferredBufferSize;
	switch (card->rate) {
		case 11025:
581
		case 22050:
582 583 584 585 586
			preferredBufferSize= .020;
			break;
		default:
			preferredBufferSize= .015;
	}
587
	[audioSession setPreferredIOBufferDuration:(NSTimeInterval)preferredBufferSize 
588
                               error:&err];
Benjamin REIS's avatar
Benjamin REIS committed
589
	if(err) ms_error("Unable to change IO buffer duration because : %s", [err localizedDescription].UTF8String);
Benjamin REIS's avatar
Benjamin REIS committed
590
	err = nil;
jehan's avatar
jehan committed
591
}
592

jehan's avatar
jehan committed
593 594 595
static void au_read_postprocess(MSFilter *f){
	au_filter_read_data_t *d= (au_filter_read_data_t*)f->data;
	ms_mutex_lock(&d->mutex);
596
	flushq(&d->rq,0);
597
	ms_ticker_set_synchronizer(f->ticker, NULL);
598
	ms_ticker_synchronizer_destroy(d->ticker_synchronizer);
599
	ms_mutex_unlock(&d->mutex);
jehan's avatar
jehan committed
600 601 602 603 604
}

static void au_read_process(MSFilter *f){
	au_filter_read_data_t *d=(au_filter_read_data_t*)f->data;
	mblk_t *m;
605
	bool_t read_something = FALSE;
606
	if (!(d->base.card->read_started=d->base.card->io_unit_started)) {
jehan's avatar
jehan committed
607 608 609
		//make sure audio unit is started
		start_audio_unit((au_filter_base_t*)d,f->ticker->time);
	}
610 611 612 613 614 615 616 617 618
	ms_mutex_lock(&d->mutex);
	while((m = getq(&d->rq)) != NULL){
		d->read_samples += (msgdsize(m) / 2) / d->base.card->nchannels;
		ms_queue_put(f->outputs[0],m);
		read_something = TRUE;
	}
	ms_mutex_unlock(&d->mutex);
	
	if (read_something) ms_ticker_synchronizer_update(d->ticker_synchronizer, d->read_samples, d->base.card->rate);
jehan's avatar
jehan committed
619 620 621 622 623 624 625 626 627
}



/***********************************write function********************/

static void au_write_preprocess(MSFilter *f){
	ms_debug("au_write_preprocess");
	OSStatus auresult;
Benjamin REIS's avatar
Benjamin REIS committed
628
	NSError *err = nil;;
jehan's avatar
jehan committed
629 630
	au_filter_write_data_t *d= (au_filter_write_data_t*)f->data;
	au_card_t* card=d->base.card;
631
	AVAudioSession *audioSession = [AVAudioSession sharedInstance];
632
	cancel_audio_unit_timer(card);
633

jehan's avatar
jehan committed
634
	if (card->io_unit_started) {
Simon Morlat's avatar
Simon Morlat committed
635
		ms_message("AudioUnit already started");
jehan's avatar
jehan committed
636 637
		return;
	}
jehan's avatar
jehan committed
638
	configure_audio_session(card, f->ticker->time);
639 640


641
	if (!card->io_unit) create_io_unit(&card->io_unit, card);
642 643 644



jehan's avatar
jehan committed
645
	AudioStreamBasicDescription audioFormat;
646
	/*card sampling rate is fixed at that time*/
647
	audioFormat.mSampleRate			= card->rate;
648 649 650
	audioFormat.mFormatID			= kAudioFormatLinearPCM;
	audioFormat.mFormatFlags		= kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
	audioFormat.mFramesPerPacket	= 1;
jehan's avatar
jehan committed
651 652 653 654 655
	audioFormat.mChannelsPerFrame	= card->nchannels;
	audioFormat.mBitsPerChannel		= card->bits;
	audioFormat.mBytesPerPacket		= card->bits / 8;
	audioFormat.mBytesPerFrame		= card->nchannels * card->bits / 8;

656 657
	UInt32 doNotSetProperty    = 0;
	UInt32 doSetProperty    = 1;
jehan's avatar
jehan committed
658 659 660
	Float64 hwsamplerate;

	//enable speaker output
661
	auresult =AudioUnitSetProperty (
jehan's avatar
jehan committed
662
									card->io_unit,
663 664 665 666 667 668
									kAudioOutputUnitProperty_EnableIO,
									kAudioUnitScope_Output ,
									outputBus,
									&doSetProperty,
									sizeof (doSetProperty)
									);
jehan's avatar
jehan committed
669
	check_au_unit_result(auresult,"kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_Output");
670

jehan's avatar
jehan committed
671 672 673 674 675 676 677 678 679
	/*enable mic for scheduling render call back, why ?*/
	auresult=AudioUnitSetProperty (/*enable mic input*/
								   card->io_unit,
								   kAudioOutputUnitProperty_EnableIO,
								   kAudioUnitScope_Input ,
								   inputBus,
								   &doSetProperty,
								   sizeof (doSetProperty)
								   );
680

jehan's avatar
jehan committed
681 682 683 684 685 686 687 688 689 690 691
	check_au_unit_result(auresult,"kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_Input");
	auresult=AudioUnitSetProperty (
								   card->io_unit,
								   kAudioUnitProperty_StreamFormat,
								   kAudioUnitScope_Output,
								   inputBus,
								   &audioFormat,
								   sizeof (audioFormat)
								   );
	check_au_unit_result(auresult,"kAudioUnitProperty_StreamFormat,kAudioUnitScope_Output");
	/*end of: enable mic for scheduling render call back, why ?*/
692

693 694
	//setup stream format
	auresult=AudioUnitSetProperty (
jehan's avatar
jehan committed
695
								   card->io_unit,
696
								   kAudioUnitProperty_StreamFormat,
697 698
								   kAudioUnitScope_Input,
								   outputBus,
699 700 701
								   &audioFormat,
								   sizeof (audioFormat)
								   );
jehan's avatar
jehan committed
702
	check_au_unit_result(auresult,"kAudioUnitProperty_StreamFormat,kAudioUnitScope_Input");
703

704 705
	//disable unit buffer allocation
	auresult=AudioUnitSetProperty (
jehan's avatar
jehan committed
706
								   card->io_unit,
707 708 709 710 711 712
								   kAudioUnitProperty_ShouldAllocateBuffer,
								   kAudioUnitScope_Output,
								   outputBus,
								   &doNotSetProperty,
								   sizeof (doNotSetProperty)
								   );
713 714 715 716 717
	check_au_unit_result(auresult,"kAudioUnitProperty_ShouldAllocateBuffer,kAudioUnitScope_Output");
	AURenderCallbackStruct renderCallbackStruct;
	renderCallbackStruct.inputProc       = au_write_cb;
	renderCallbackStruct.inputProcRefCon = card;

718
	auresult=AudioUnitSetProperty (
719 720 721 722 723 724
								   card->io_unit,
								   kAudioUnitProperty_SetRenderCallback,
								   kAudioUnitScope_Input,
								   outputBus,
								   &renderCallbackStruct,
								   sizeof (renderCallbackStruct)
725
								   );
jehan's avatar
jehan committed
726
	check_au_unit_result(auresult,"kAudioUnitProperty_SetRenderCallback,kAudioUnitScope_Input");
727
	hwsamplerate = audioSession.sampleRate;
728

729 730 731 732 733 734 735 736
	/*
	 * Bluetooth bug: on iphone5s at least, some low end BT headset create a bug in iOS when requesting 48khz hardware sampling rate.
	 * These headset don't support this frequency, which result in the hardware sampling rate finally set to 8khz by iOS.
	 * The bug traduces in no audio chunks that can be pushed or pulled from the AudioUnit.
	 *
	 * Apparently the driver doesn't recover from this situation.
	 * The workaround is then to request 44100 Hz instead of 48khz.
	 */
Benjamin REIS's avatar
Benjamin REIS committed
737
	if(card->rate == 8000 && (floor(NSFoundationVersionNumber) >= NSFoundationVersionNumber_iOS_9_x_Max)) {
738
		hwsamplerate=48000;
739
		[audioSession setPreferredSampleRate:hwsamplerate error:&err];
Benjamin REIS's avatar
Benjamin REIS committed
740
		if(err) ms_error("Unable to change sample rate because : %s", [err localizedDescription].UTF8String);
Benjamin REIS's avatar
Benjamin REIS committed
741
		err = nil;
742 743 744
		return;
	}

745
	if( hwsamplerate != card->rate) {
746
		if(card->rate <= 44100 || (floor(NSFoundationVersionNumber) >= NSFoundationVersionNumber_iOS_9_x_Max)){
747
			hwsamplerate=card->rate;
748
			[audioSession setPreferredSampleRate:hwsamplerate error:&err];
Benjamin REIS's avatar
Benjamin REIS committed
749
			if(err) ms_error("Unable to change sample rate because : %s", [err localizedDescription].UTF8String);
Benjamin REIS's avatar
Benjamin REIS committed
750
			err = nil;
751
		} else {
752
			ms_message("Not applying PreferredSampleRate because asked rate is too high [%i]",((int)hwsamplerate));
753 754
		}
	} else {
755
		ms_message("Not applying PreferredSampleRate because HW rate already correct [%i]",((int)hwsamplerate));
756
	}
757 758
}

jehan's avatar
jehan committed
759 760 761
static void au_write_postprocess(MSFilter *f){
	ms_debug("au_write_postprocess");
	au_filter_write_data_t *d= (au_filter_write_data_t*)f->data;
762
	ms_mutex_lock(&d->mutex);
763
	ms_flow_controlled_bufferizer_flush(d->bufferizer);
764 765 766 767 768 769 770
	ms_mutex_unlock(&d->mutex);
}



static void au_write_process(MSFilter *f){
	ms_debug("au_write_process");
jehan's avatar
jehan committed
771
	au_filter_write_data_t *d=(au_filter_write_data_t*)f->data;
772 773

	if (!(d->base.card->write_started=d->base.card->io_unit_started)) {
jehan's avatar
jehan committed
774 775 776
		//make sure audio unit is started
		start_audio_unit((au_filter_base_t*)d,f->ticker->time);
	}
777 778 779 780 781

	if (d->base.muted){
		ms_queue_flush(f->inputs[0]);
		return;
	}
782 783 784
	ms_mutex_lock(&d->mutex);
	ms_flow_controlled_bufferizer_put_from_queue(d->bufferizer, f->inputs[0]);
	ms_mutex_unlock(&d->mutex);
785 786 787 788 789
}

static int set_rate(MSFilter *f, void *arg){
	int proposed_rate = *((int*)arg);
	ms_debug("set_rate %d",proposed_rate);
jehan's avatar
jehan committed
790
	au_filter_base_t *d=(au_filter_base_t*)f->data;
791
	if (proposed_rate != d->card->rate){
jehan's avatar
jehan committed
792
		return -1;//only support 1 rate
793 794
	} else {
		return 0;
jehan's avatar
jehan committed
795
	}
796 797 798
}

static int get_rate(MSFilter *f, void *data){
jehan's avatar
jehan committed
799
	au_filter_base_t *d=(au_filter_base_t*)f->data;
800
	*(int*)data=d->card->rate;
801 802 803 804
	return 0;
}


805
static int read_set_nchannels(MSFilter *f, void *arg){
806
	ms_debug("set_nchannels %d", *((int*)arg));
807
	au_filter_base_t *d=(au_filter_base_t*)f->data;
jehan's avatar
jehan committed
808
	d->card->nchannels=*(int*)arg;
809 810 811
	return 0;
}

812 813 814 815 816 817 818 819
static int write_set_nchannels(MSFilter *f, void *arg){
	ms_debug("set_nchannels %d", *((int*)arg));
	au_filter_write_data_t *d=(au_filter_write_data_t*)f->data;
	d->base.card->nchannels=*(int*)arg;
	ms_flow_controlled_bufferizer_set_nchannels(d->bufferizer, d->base.card->nchannels);
	return 0;
}

820 821 822 823 824 825
static int get_nchannels(MSFilter *f, void *data) {
	au_filter_base_t *d=(au_filter_base_t *)f->data;
	*(int *)data = d->card->nchannels;
	return 0;
}

826 827 828 829 830 831
static int set_muted(MSFilter *f, void *data){
	au_filter_base_t *d=(au_filter_base_t*)f->data;
	d->muted = *(int*)data;
	return 0;
}

832 833 834 835 836 837 838 839 840
static MSFilterMethod au_read_methods[]={
	{	MS_FILTER_SET_SAMPLE_RATE	, set_rate	},
	{	MS_FILTER_GET_SAMPLE_RATE	, get_rate	},
	{	MS_FILTER_SET_NCHANNELS		, read_set_nchannels	},
	{	MS_FILTER_GET_NCHANNELS		, get_nchannels	},
	{	0				, NULL		}
};

static MSFilterMethod au_write_methods[]={
841 842
	{	MS_FILTER_SET_SAMPLE_RATE	, set_rate	},
	{	MS_FILTER_GET_SAMPLE_RATE	, get_rate	},
843
	{	MS_FILTER_SET_NCHANNELS		, write_set_nchannels	},
844
	{	MS_FILTER_GET_NCHANNELS		, get_nchannels	},
Simon Morlat's avatar
Simon Morlat committed
845
	{	MS_AUDIO_PLAYBACK_MUTE	 	, set_muted	},
846 847
	{	0				, NULL		}
};
848 849 850 851 852 853 854

static void shutdown_timer(CFRunLoopTimerRef timer, void *info){
	au_card_t *card=(au_card_t*)info;
	stop_audio_unit(card);
}

static void check_unused(au_card_t *card){
855 856
	if (card->read_data==NULL && card->write_data==NULL ){

857
		if (!card->is_tester && card->shutdown_timer==NULL && card->use_shutdowntimer){
858 859 860 861 862 863 864 865 866 867 868 869 870
			/*program the shutdown of the audio unit in a few seconds*/
			CFRunLoopTimerContext ctx={0};
			ctx.info=card;
			card->shutdown_timer=CFRunLoopTimerCreate (
												kCFAllocatorDefault,
												CFAbsoluteTimeGetCurrent() + 2.5,
												0,
												0,
												0,
												shutdown_timer,
												&ctx
												);
			CFRunLoopAddTimer(CFRunLoopGetMain(), card->shutdown_timer,kCFRunLoopCommonModes);
871
		} else if( card->is_tester ) {
872 873
			stop_audio_unit(card);
		}
874 875 876
	}
}

jehan's avatar
jehan committed
877 878
static void au_read_uninit(MSFilter *f) {
	au_filter_read_data_t *d=(au_filter_read_data_t*)f->data;
jehan's avatar
jehan committed
879
	au_card_t* card=d->base.card;
880

881
	ms_mutex_lock(&card->mutex);
jehan's avatar
jehan committed
882
	card->read_data=NULL;
883
	ms_mutex_unlock(&card->mutex);
884 885 886

	check_unused(card);

jehan's avatar
jehan committed
887
	ms_mutex_destroy(&d->mutex);
888 889

	flushq(&d->rq,0);
jehan's avatar
jehan committed
890 891 892 893 894
	ms_free(d);
}

static void au_write_uninit(MSFilter *f) {
	au_filter_write_data_t *d=(au_filter_write_data_t*)f->data;
jehan's avatar
jehan committed
895
	au_card_t* card=d->base.card;
896

897
	ms_mutex_lock(&card->mutex);
jehan's avatar
jehan committed
898
	card->write_data=NULL;
899
	ms_mutex_unlock(&card->mutex);
900

901
	check_unused(card);
902

jehan's avatar
jehan committed
903
	ms_mutex_destroy(&d->mutex);
904
	ms_flow_controlled_bufferizer_destroy(d->bufferizer);
jehan's avatar
jehan committed
905 906
	ms_free(d);
}
907 908 909 910

MSFilterDesc au_read_desc={
.id=MS_IOUNIT_READ_ID,
.name="MSAURead",
Simon Morlat's avatar
Simon Morlat committed
911
.text=N_("Sound capture filter for iOS Audio Unit Service"),
912 913 914 915 916 917
.category=MS_FILTER_OTHER,
.ninputs=0,
.noutputs=1,
.preprocess=au_read_preprocess,
.process=au_read_process,
.postprocess=au_read_postprocess,
jehan's avatar
jehan committed
918
.uninit=au_read_uninit,
919
.methods=au_read_methods
920 921 922 923 924 925
};


MSFilterDesc au_write_desc={
.id=MS_IOUNIT_WRITE_ID,
.name="MSAUWrite",
Simon Morlat's avatar
Simon Morlat committed
926
.text=N_("Sound playback filter for iOS Audio Unit Service"),
927 928 929 930 931 932
.category=MS_FILTER_OTHER,
.ninputs=1,
.noutputs=0,
.preprocess=au_write_preprocess,
.process=au_write_process,
.postprocess=au_write_postprocess,
jehan's avatar
jehan committed
933
.uninit=au_write_uninit,
934
.methods=au_write_methods
935 936
};

jehan's avatar
jehan committed
937
static MSFilter *ms_au_read_new(MSSndCard *mscard){
938
	ms_debug("ms_au_read_new");
jehan's avatar
jehan committed
939
	au_card_t* card=(au_card_t*)(mscard->data);
940
	MSFilter *f=ms_factory_create_filter_from_desc(ms_snd_card_get_factory(mscard), &au_read_desc);
jehan's avatar
jehan committed
941 942 943 944
	au_filter_read_data_t *d=ms_new0(au_filter_read_data_t,1);
	qinit(&d->rq);
	d->readTimeStamp.mSampleTime=-1;
	ms_mutex_init(&d->mutex,NULL);
jehan's avatar
jehan committed
945 946
	d->base.card=card;
	card->read_data=d;
jehan's avatar
jehan committed
947
	f->data=d;
948 949 950
	return f;
}

jehan's avatar
jehan committed
951
static MSFilter *ms_au_write_new(MSSndCard *mscard){
952
	ms_debug("ms_au_write_new");
jehan's avatar
jehan committed
953
	au_card_t* card=(au_card_t*)(mscard->data);
954
	MSFilter *f=ms_factory_create_filter_from_desc(ms_snd_card_get_factory(mscard), &au_write_desc);
jehan's avatar
jehan committed
955
	au_filter_write_data_t *d=ms_new0(au_filter_write_data_t,1);
956 957 958
	d->bufferizer= ms_flow_controlled_bufferizer_new(f, card->rate, card->nchannels);
	ms_flow_controlled_bufferizer_set_max_size_ms(d->bufferizer, flowControlThreshold);
	ms_flow_controlled_bufferizer_set_flow_control_interval_ms(d->bufferizer, flowControlInterval);
jehan's avatar
jehan committed
959
	ms_mutex_init(&d->mutex,NULL);
jehan's avatar
jehan committed
960 961
	d->base.card=card;
	card->write_data=d;
jehan's avatar
jehan committed
962
	f->data=d;
963

964
	if (card->rate == 0){ /*iounit stopped set initial value*/
965
		card->rate=ms_snd_card_get_preferred_sample_rate(card->ms_snd_card);
966
		ms_flow_controlled_bufferizer_set_samplerate(d->bufferizer, card->rate);
967
	}
968 969 970
	return f;
}

jehan's avatar
jehan committed
971

972 973
MS_FILTER_DESC_EXPORT(au_read_desc)
MS_FILTER_DESC_EXPORT(au_write_desc)