Subversion Repositories Mobile Apps.GyroMouse

Rev

Blame | Last modification | View Log | Download | RSS feed

  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
  265.