//
// ISSoundAdditions.
m (ver 1.2 - 2012.10.27
)
//
// Created by Massimo Moiso (2012-09) InerziaSoft
// based on an idea of Antonio Nunes, SintraWorks
//
// Permission
is granted free of charge to use this code without restriction
// and without limitation, with the only condition that the copyright
// notice and this permission shall be included in
all copies.
//
// THE SOFTWARE
IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ISSoundAdditions.h"
AudioDeviceID obtainDefaultOutputDevice (void);
@implementation NSSound (ISSoundAdditions)
//
//
Return the ID of the default audio device; this
is a C routine
//
// IN: none
// OUT: the ID of the default device or AudioObjectUnknown
//
AudioDeviceID obtainDefaultOutputDevice (void)
{
AudioDeviceID theAnswer = kAudioObjectUnknown;
UInt32 theSize = sizeof
(AudioDeviceID
);
AudioObjectPropertyAddress theAddress;
theAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
theAddress.mScope = kAudioObjectPropertyScopeGlobal;
theAddress.mElement = kAudioObjectPropertyElementMaster;
//first be sure that a default device exists
if (! AudioObjectHasProperty(kAudioObjectSystemObject, &theAddress) ) {
NSLog
(@"Unable to
get default audio device"
);
return theAnswer;
}
//
get the property
'default output device'
OSStatus theError = AudioObjectGetPropertyData
(kAudioObjectSystemObject, &theAddress,
0,
NULL, &theSize, &theAnswer
);
if (theError != noErr) {
NSLog
(@"Unable to
get output audio device"
);
return theAnswer;
}
return theAnswer;
}
//
//
Return the ID of the default audio device; this
is a category
class method
// that can be called from outside
//
// IN: none
// OUT: the ID of the default device or AudioObjectUnknown
//
+ (AudioDeviceID)defaultOutputDevice
{
return obtainDefaultOutputDevice();
}
//
//
Return the system
sound volume as a float in the range
[0...1]
//
// IN: none
// OUT: (float) the volume of the default device
//
+ (float)systemVolume
{
AudioDeviceID defaultDevID = kAudioObjectUnknown;
UInt32 theSize = sizeof
(Float32
);
OSStatus theError;
Float32 theVolume = 0;
AudioObjectPropertyAddress theAddress;
defaultDevID = obtainDefaultOutputDevice();
if (defaultDevID == kAudioObjectUnknown) return 0.0; //device not found: return 0
theAddress.mSelector = kAudioHardwareServiceDeviceProperty_VirtualMainVolume;
theAddress.mScope = kAudioDevicePropertyScopeOutput;
theAddress.mElement = kAudioObjectPropertyElementMaster;
//be sure that the default device has the volume property
if (! AudioObjectHasProperty(defaultDevID, &theAddress) ) {
NSLog(@"No volume control for device 0x%0x",defaultDevID);
return 0.0;
}
//
now read the property and correct it,
if outside
[0...1]
theError = AudioObjectGetPropertyData
(defaultDevID, &theAddress,
0,
NULL, &theSize, &theVolume
);
if ( theError != noErr ) {
NSLog(@"Unable to read volume for device 0x%0x", defaultDevID);
return 0.0;
}
theVolume = theVolume > 1.0 ? 1.0 : (theVolume < 0.0 ? 0.0 : theVolume);
return theVolume;
}
//
//
Set the volume of the default device
//
// IN: (float)the new volume
// OUT: none
//
+ (void)setSystemVolume:(float)theVolume
{
float newValue = theVolume;
AudioObjectPropertyAddress theAddress;
AudioDeviceID defaultDevID;
OSStatus theError = noErr;
Boolean canSetVol = YES, muteValue;
Boolean hasMute = YES, canMute = YES;
defaultDevID = obtainDefaultOutputDevice();
if (defaultDevID == kAudioObjectUnknown
) { //device not found:
return without trying to
set
NSLog(@"Device unknown");
return;
}
//check
if the new value
is in the correct range - normalize it
if not
newValue = theVolume > 1.0 ? 1.0 : (theVolume < 0.0 ? 0.0 : theVolume);
if (newValue != theVolume) {
NSLog(@"Tentative volume (%5.2f) was out of range; reset to %5.2f", theVolume, newValue);
}
theAddress.mElement = kAudioObjectPropertyElementMaster;
theAddress.mScope = kAudioDevicePropertyScopeOutput;
//
set the selector to mute or not by checking
if under threshold and check
if a mute command
is available
if ( (muteValue = (newValue < THRESHOLD)) )
{
theAddress.mSelector = kAudioDevicePropertyMute;
hasMute = AudioObjectHasProperty(defaultDevID, &theAddress);
if (hasMute)
{
theError = AudioObjectIsPropertySettable(defaultDevID, &theAddress, &canMute);
if (theError != noErr || !canMute)
{
canMute = NO;
NSLog(@"Should mute device 0x%0x but did not success",defaultDevID);
}
}
else canMute = NO;
}
else
{
theAddress.mSelector = kAudioHardwareServiceDeviceProperty_VirtualMainVolume;
}
// ****
now manage the volume following the
what we found ****
//be sure the device has a volume command
if (! AudioObjectHasProperty(defaultDevID, &theAddress))
{
NSLog(@"The device 0x%0x does not have a volume to set", defaultDevID);
return;
}
//be sure the device can
set the volume
theError = AudioObjectIsPropertySettable(defaultDevID, &theAddress, &canSetVol);
if ( theError!=noErr || !canSetVol )
{
NSLog(@"The volume of device 0x%0x cannot be set", defaultDevID);
return;
}
//if under the threshold then mute it, only if possible - done/exit
if (muteValue && hasMute && canMute)
{
muted = 1;
theError = AudioObjectSetPropertyData
(defaultDevID, &theAddress,
0,
NULL, sizeof
(muted
), &muted
);
if (theError != noErr)
{
NSLog(@"The device 0x%0x was not muted",defaultDevID);
return;
}
}
{
theError = AudioObjectSetPropertyData
(defaultDevID, &theAddress,
0,
NULL, sizeof
(newValue
), &newValue
);
if (theError != noErr)
{
NSLog(@"The device 0x%0x was unable to set volume", defaultDevID);
}
//
if device
is able to handle muting, maybe it was muted, so unlock it
if (hasMute && canMute)
{
theAddress.mSelector = kAudioDevicePropertyMute;
muted = 0;
theError = AudioObjectSetPropertyData
(defaultDevID, &theAddress,
0,
NULL, sizeof
(muted
), &muted
);
}
}
if (theError != noErr) {
NSLog
(@"Unable to
set volume
for device 0x
%0x", defaultDevID);
}
}
//
// Increase the volume of the system device by a certain value
//
// IN: (float) amount of volume to increase
// OUT: none
//
+ (void)increaseSystemVolumeBy:(float)amount {
[self setSystemVolume:self.systemVolume+amount];
}
//
// Decrease the volume of the system device by a certain value
//
// IN: (float) amount of volume to decrease
// OUT: none
//
+ (void)decreaseSystemVolumeBy:(float)amount {
[self setSystemVolume:self.systemVolume-amount];
}
//
// IN:
(Boolean
) if true the device
is muted, false it
is unmated
// OUT: none
//
+ (void)applyMute:(Boolean)m
{
AudioDeviceID defaultDevID = kAudioObjectUnknown;
AudioObjectPropertyAddress theAddress;
Boolean hasMute, canMute = YES;
OSStatus theError = noErr;
defaultDevID = obtainDefaultOutputDevice();
if (defaultDevID == kAudioObjectUnknown) { //device not found
NSLog(@"Device unknown");
return;
}
theAddress.mElement = kAudioObjectPropertyElementMaster;
theAddress.mScope = kAudioDevicePropertyScopeOutput;
theAddress.mSelector = kAudioDevicePropertyMute;
muted = m ? 1 : 0;
hasMute = AudioObjectHasProperty(defaultDevID, &theAddress);
if (hasMute)
{
theError = AudioObjectIsPropertySettable(defaultDevID, &theAddress, &canMute);
if (theError == noErr && canMute)
{
theError = AudioObjectSetPropertyData
(defaultDevID, &theAddress,
0,
NULL, sizeof
(muted
), &muted
);
if (theError != noErr) NSLog(@"Cannot change mute status of device 0x%0x", defaultDevID);
}
}
}
@end