1 /**
2  * @file sess.c  AudioUnit sound driver - session
3  *
4  * Copyright (C) 2010 Creytiv.com
5  */
6 #include <AudioUnit/AudioUnit.h>
7 #include <AudioToolbox/AudioToolbox.h>
8 #include <re.h>
9 #include <baresip.h>
10 #include "audiounit.h"
11 
12 
13 struct audiosess {
14 	struct list sessl;
15 };
16 
17 
18 struct audiosess_st {
19 	struct audiosess *as;
20 	struct le le;
21 	audiosess_int_h *inth;
22 	void *arg;
23 };
24 
25 
26 static struct audiosess *gas;
27 
28 
29 #if TARGET_OS_IPHONE
propListener(void * inClientData,AudioSessionPropertyID inID,UInt32 inDataSize,const void * inData)30 static void propListener(void *inClientData, AudioSessionPropertyID inID,
31 			 UInt32 inDataSize, const void *inData)
32 {
33 	struct audiosess *sess = inClientData;
34 	CFDictionaryRef dref = inData;
35 	CFNumberRef nref;
36 	SInt32 reason = 0;
37 
38 	(void)inDataSize;
39 	(void)sess;
40 
41 	if (kAudioSessionProperty_AudioRouteChange != inID)
42 		return;
43 
44 	nref = CFDictionaryGetValue(
45 			dref,
46 			CFSTR(kAudioSession_AudioRouteChangeKey_Reason)
47 			);
48 
49 	CFNumberGetValue(nref, kCFNumberSInt32Type, &reason);
50 
51 	info("audiounit: AudioRouteChange - reason %d\n", reason);
52 }
53 #endif
54 
55 
sess_destructor(void * arg)56 static void sess_destructor(void *arg)
57 {
58 	struct audiosess_st *st = arg;
59 
60 	list_unlink(&st->le);
61 	mem_deref(st->as);
62 }
63 
64 
destructor(void * arg)65 static void destructor(void *arg)
66 {
67 	struct audiosess *as = arg;
68 #if TARGET_OS_IPHONE
69 	AudioSessionPropertyID id = kAudioSessionProperty_AudioRouteChange;
70 
71 	AudioSessionRemovePropertyListenerWithUserData(id, propListener, as);
72 	AudioSessionSetActive(false);
73 #endif
74 
75 	list_flush(&as->sessl);
76 
77 	gas = NULL;
78 }
79 
80 
audiosess_alloc(struct audiosess_st ** stp,audiosess_int_h * inth,void * arg)81 int audiosess_alloc(struct audiosess_st **stp,
82 		    audiosess_int_h *inth, void *arg)
83 {
84 	struct audiosess_st *st = NULL;
85 	struct audiosess *as = NULL;
86 	int err = 0;
87 	bool created = false;
88 #if TARGET_OS_IPHONE
89 	AudioSessionPropertyID id = kAudioSessionProperty_AudioRouteChange;
90 	UInt32 category;
91 	OSStatus ret;
92 #endif
93 
94 	if (!stp)
95 		return EINVAL;
96 
97 #if TARGET_OS_IPHONE
98 	/* Must be done for all modules */
99 	category = kAudioSessionCategory_PlayAndRecord;
100 	ret = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
101 				      sizeof(category), &category);
102 	if (ret) {
103 		warning("audiounit: Audio Category: %d\n", ret);
104 		return EINVAL;
105 	}
106 #endif
107 
108 	if (gas)
109 		goto makesess;
110 
111 	as = mem_zalloc(sizeof(*as), destructor);
112 	if (!as)
113 		return ENOMEM;
114 
115 #if TARGET_OS_IPHONE
116 	ret = AudioSessionSetActive(true);
117 	if (ret) {
118 		warning("audiounit: AudioSessionSetActive: %d\n", ret);
119 		err = ENOSYS;
120 		goto out;
121 	}
122 
123 	ret = AudioSessionAddPropertyListener(id, propListener, as);
124 	if (ret) {
125 		warning("audiounit: AudioSessionAddPropertyListener: %d\n",
126 			ret);
127 		err = EINVAL;
128 		goto out;
129 	}
130 #endif
131 
132 	gas = as;
133 	created = true;
134 
135  makesess:
136 	st = mem_zalloc(sizeof(*st), sess_destructor);
137 	if (!st) {
138 		err = ENOMEM;
139 		goto out;
140 	}
141 	st->inth = inth;
142 	st->arg = arg;
143 	st->as = created ? gas : mem_ref(gas);
144 
145 	list_append(&gas->sessl, &st->le, st);
146 
147  out:
148 	if (err) {
149 		mem_deref(as);
150 		mem_deref(st);
151 	}
152 	else {
153 		*stp = st;
154 	}
155 
156 	return err;
157 }
158 
159 
audiosess_interrupt(bool start)160 void audiosess_interrupt(bool start)
161 {
162 	struct le *le;
163 
164 	if (!gas)
165 		return;
166 
167 	for (le = gas->sessl.head; le; le = le->next) {
168 
169 		struct audiosess_st *st = le->data;
170 
171 		if (st->inth)
172 			st->inth(start, st->arg);
173 	}
174 }
175