Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | // |
2 | // ISSoundAdditions.m (ver 1.2 - 2012.10.27) |
||
3 | // |
||
4 | // Created by Massimo Moiso (2012-09) InerziaSoft |
||
5 | // based on an idea of Antonio Nunes, SintraWorks |
||
6 | // |
||
7 | // Permission is granted free of charge to use this code without restriction |
||
8 | // and without limitation, with the only condition that the copyright |
||
9 | // notice and this permission shall be included in all copies. |
||
10 | // |
||
11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||
12 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||
13 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||
14 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||
15 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||
16 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||
17 | // THE SOFTWARE. |
||
18 | |||
19 | |||
20 | #import "ISSoundAdditions.h" |
||
21 | |||
22 | AudioDeviceID obtainDefaultOutputDevice (void); |
||
23 | |||
24 | @implementation NSSound (ISSoundAdditions) |
||
25 | |||
26 | // |
||
27 | // Return the ID of the default audio device; this is a C routine |
||
28 | // |
||
29 | // IN: none |
||
30 | // OUT: the ID of the default device or AudioObjectUnknown |
||
31 | // |
||
32 | AudioDeviceID obtainDefaultOutputDevice (void) |
||
33 | { |
||
34 | AudioDeviceID theAnswer = kAudioObjectUnknown; |
||
35 | UInt32 theSize = sizeof(AudioDeviceID); |
||
36 | AudioObjectPropertyAddress theAddress; |
||
37 | |||
38 | theAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; |
||
39 | theAddress.mScope = kAudioObjectPropertyScopeGlobal; |
||
40 | theAddress.mElement = kAudioObjectPropertyElementMaster; |
||
41 | |||
42 | //first be sure that a default device exists |
||
43 | if (! AudioObjectHasProperty(kAudioObjectSystemObject, &theAddress) ) { |
||
44 | NSLog(@"Unable to get default audio device"); |
||
45 | return theAnswer; |
||
46 | } |
||
47 | //get the property 'default output device' |
||
48 | OSStatus theError = AudioObjectGetPropertyData(kAudioObjectSystemObject, &theAddress, 0, NULL, &theSize, &theAnswer); |
||
49 | if (theError != noErr) { |
||
50 | NSLog(@"Unable to get output audio device"); |
||
51 | return theAnswer; |
||
52 | } |
||
53 | return theAnswer; |
||
54 | } |
||
55 | |||
56 | // |
||
57 | // Return the ID of the default audio device; this is a category class method |
||
58 | // that can be called from outside |
||
59 | // |
||
60 | // IN: none |
||
61 | // OUT: the ID of the default device or AudioObjectUnknown |
||
62 | // |
||
63 | + (AudioDeviceID)defaultOutputDevice |
||
64 | { |
||
65 | return obtainDefaultOutputDevice(); |
||
66 | } |
||
67 | |||
68 | |||
69 | // |
||
70 | // Return the system sound volume as a float in the range [0...1] |
||
71 | // |
||
72 | // IN: none |
||
73 | // OUT: (float) the volume of the default device |
||
74 | // |
||
75 | + (float)systemVolume |
||
76 | { |
||
77 | AudioDeviceID defaultDevID = kAudioObjectUnknown; |
||
78 | UInt32 theSize = sizeof(Float32); |
||
79 | OSStatus theError; |
||
80 | Float32 theVolume = 0; |
||
81 | AudioObjectPropertyAddress theAddress; |
||
82 | |||
83 | defaultDevID = obtainDefaultOutputDevice(); |
||
84 | if (defaultDevID == kAudioObjectUnknown) return 0.0; //device not found: return 0 |
||
85 | |||
86 | theAddress.mSelector = kAudioHardwareServiceDeviceProperty_VirtualMainVolume; |
||
87 | theAddress.mScope = kAudioDevicePropertyScopeOutput; |
||
88 | theAddress.mElement = kAudioObjectPropertyElementMaster; |
||
89 | |||
90 | //be sure that the default device has the volume property |
||
91 | if (! AudioObjectHasProperty(defaultDevID, &theAddress) ) { |
||
92 | NSLog(@"No volume control for device 0x%0x",defaultDevID); |
||
93 | return 0.0; |
||
94 | } |
||
95 | |||
96 | //now read the property and correct it, if outside [0...1] |
||
97 | theError = AudioObjectGetPropertyData(defaultDevID, &theAddress, 0, NULL, &theSize, &theVolume); |
||
98 | if ( theError != noErr ) { |
||
99 | NSLog(@"Unable to read volume for device 0x%0x", defaultDevID); |
||
100 | return 0.0; |
||
101 | } |
||
102 | theVolume = theVolume > 1.0 ? 1.0 : (theVolume < 0.0 ? 0.0 : theVolume); |
||
103 | |||
104 | return theVolume; |
||
105 | } |
||
106 | |||
107 | // |
||
108 | // Set the volume of the default device |
||
109 | // |
||
110 | // IN: (float)the new volume |
||
111 | // OUT: none |
||
112 | // |
||
113 | + (void)setSystemVolume:(float)theVolume |
||
114 | { |
||
115 | float newValue = theVolume; |
||
116 | AudioObjectPropertyAddress theAddress; |
||
117 | AudioDeviceID defaultDevID; |
||
118 | OSStatus theError = noErr; |
||
119 | UInt32 muted; |
||
120 | Boolean canSetVol = YES, muteValue; |
||
121 | Boolean hasMute = YES, canMute = YES; |
||
122 | |||
123 | defaultDevID = obtainDefaultOutputDevice(); |
||
124 | if (defaultDevID == kAudioObjectUnknown) { //device not found: return without trying to set |
||
125 | NSLog(@"Device unknown"); |
||
126 | return; |
||
127 | } |
||
128 | |||
129 | //check if the new value is in the correct range - normalize it if not |
||
130 | newValue = theVolume > 1.0 ? 1.0 : (theVolume < 0.0 ? 0.0 : theVolume); |
||
131 | if (newValue != theVolume) { |
||
132 | NSLog(@"Tentative volume (%5.2f) was out of range; reset to %5.2f", theVolume, newValue); |
||
133 | } |
||
134 | |||
135 | theAddress.mElement = kAudioObjectPropertyElementMaster; |
||
136 | theAddress.mScope = kAudioDevicePropertyScopeOutput; |
||
137 | |||
138 | //set the selector to mute or not by checking if under threshold and check if a mute command is available |
||
139 | if ( (muteValue = (newValue < THRESHOLD)) ) |
||
140 | { |
||
141 | theAddress.mSelector = kAudioDevicePropertyMute; |
||
142 | hasMute = AudioObjectHasProperty(defaultDevID, &theAddress); |
||
143 | if (hasMute) |
||
144 | { |
||
145 | theError = AudioObjectIsPropertySettable(defaultDevID, &theAddress, &canMute); |
||
146 | if (theError != noErr || !canMute) |
||
147 | { |
||
148 | canMute = NO; |
||
149 | NSLog(@"Should mute device 0x%0x but did not success",defaultDevID); |
||
150 | } |
||
151 | } |
||
152 | else canMute = NO; |
||
153 | } |
||
154 | else |
||
155 | { |
||
156 | theAddress.mSelector = kAudioHardwareServiceDeviceProperty_VirtualMainVolume; |
||
157 | } |
||
158 | |||
159 | // **** now manage the volume following the what we found **** |
||
160 | |||
161 | //be sure the device has a volume command |
||
162 | if (! AudioObjectHasProperty(defaultDevID, &theAddress)) |
||
163 | { |
||
164 | NSLog(@"The device 0x%0x does not have a volume to set", defaultDevID); |
||
165 | return; |
||
166 | } |
||
167 | |||
168 | //be sure the device can set the volume |
||
169 | theError = AudioObjectIsPropertySettable(defaultDevID, &theAddress, &canSetVol); |
||
170 | if ( theError!=noErr || !canSetVol ) |
||
171 | { |
||
172 | NSLog(@"The volume of device 0x%0x cannot be set", defaultDevID); |
||
173 | return; |
||
174 | } |
||
175 | |||
176 | //if under the threshold then mute it, only if possible - done/exit |
||
177 | if (muteValue && hasMute && canMute) |
||
178 | { |
||
179 | muted = 1; |
||
180 | theError = AudioObjectSetPropertyData(defaultDevID, &theAddress, 0, NULL, sizeof(muted), &muted); |
||
181 | if (theError != noErr) |
||
182 | { |
||
183 | NSLog(@"The device 0x%0x was not muted",defaultDevID); |
||
184 | return; |
||
185 | } |
||
186 | } |
||
187 | else //else set it |
||
188 | { |
||
189 | theError = AudioObjectSetPropertyData(defaultDevID, &theAddress, 0, NULL, sizeof(newValue), &newValue); |
||
190 | if (theError != noErr) |
||
191 | { |
||
192 | NSLog(@"The device 0x%0x was unable to set volume", defaultDevID); |
||
193 | } |
||
194 | //if device is able to handle muting, maybe it was muted, so unlock it |
||
195 | if (hasMute && canMute) |
||
196 | { |
||
197 | theAddress.mSelector = kAudioDevicePropertyMute; |
||
198 | muted = 0; |
||
199 | theError = AudioObjectSetPropertyData(defaultDevID, &theAddress, 0, NULL, sizeof(muted), &muted); |
||
200 | } |
||
201 | } |
||
202 | if (theError != noErr) { |
||
203 | NSLog(@"Unable to set volume for device 0x%0x", defaultDevID); |
||
204 | } |
||
205 | } |
||
206 | |||
207 | |||
208 | // |
||
209 | // Increase the volume of the system device by a certain value |
||
210 | // |
||
211 | // IN: (float) amount of volume to increase |
||
212 | // OUT: none |
||
213 | // |
||
214 | + (void)increaseSystemVolumeBy:(float)amount { |
||
215 | [self setSystemVolume:self.systemVolume+amount]; |
||
216 | } |
||
217 | |||
218 | // |
||
219 | // Decrease the volume of the system device by a certain value |
||
220 | // |
||
221 | // IN: (float) amount of volume to decrease |
||
222 | // OUT: none |
||
223 | // |
||
224 | + (void)decreaseSystemVolumeBy:(float)amount { |
||
225 | [self setSystemVolume:self.systemVolume-amount]; |
||
226 | } |
||
227 | |||
228 | // |
||
229 | // IN: (Boolean) if true the device is muted, false it is unmated |
||
230 | // OUT: none |
||
231 | // |
||
232 | + (void)applyMute:(Boolean)m |
||
233 | { |
||
234 | AudioDeviceID defaultDevID = kAudioObjectUnknown; |
||
235 | AudioObjectPropertyAddress theAddress; |
||
236 | Boolean hasMute, canMute = YES; |
||
237 | OSStatus theError = noErr; |
||
238 | UInt32 muted = 0; |
||
239 | |||
240 | defaultDevID = obtainDefaultOutputDevice(); |
||
241 | if (defaultDevID == kAudioObjectUnknown) { //device not found |
||
242 | NSLog(@"Device unknown"); |
||
243 | return; |
||
244 | } |
||
245 | |||
246 | theAddress.mElement = kAudioObjectPropertyElementMaster; |
||
247 | theAddress.mScope = kAudioDevicePropertyScopeOutput; |
||
248 | theAddress.mSelector = kAudioDevicePropertyMute; |
||
249 | muted = m ? 1 : 0; |
||
250 | |||
251 | hasMute = AudioObjectHasProperty(defaultDevID, &theAddress); |
||
252 | |||
253 | if (hasMute) |
||
254 | { |
||
255 | theError = AudioObjectIsPropertySettable(defaultDevID, &theAddress, &canMute); |
||
256 | if (theError == noErr && canMute) |
||
257 | { |
||
258 | theError = AudioObjectSetPropertyData(defaultDevID, &theAddress, 0, NULL, sizeof(muted), &muted); |
||
259 | if (theError != noErr) NSLog(@"Cannot change mute status of device 0x%0x", defaultDevID); |
||
260 | } |
||
261 | } |
||
262 | } |
||
263 | |||
264 | @end |