// static
bool    BGMDeviceControlSync::SetVirtualMasterVolume(CAHALAudioDevice inDevice, AudioObjectPropertyScope inScope, Float32 inVolume)
{
    // TODO: For me, setting the virtual master volume sets all the device's channels to the same volume, meaning you can't
    //       keep any channels quieter than the others. The expected behaviour is to scale the channel volumes
    //       proportionally. So to do this properly I think we'd have to store BGMDevice's previous volume and calculate
    //       each channel's new volume from its current volume and the distance between BGMDevice's old and new volumes.
    //
    //       The docs kAudioHardwareServiceDeviceProperty_VirtualMasterVolume for say
    //           "If the device has individual channel volume controls, this property will apply to those identified by the
    //           device's preferred multi-channel layout (or preferred stereo pair if the device is stereo only). Note that
    //           this control maintains the relative balance between all the channels it affects.
    //       so I'm not sure why that's not working here. As a workaround we take the to device's (virtual master) balance
    //       before changing the volume and set it back after, but of course that'll only work for stereo devices.
    
    bool didSetVolume = false;
    AudioObjectPropertyAddress virtualMasterVolumeAddress =
        { kAudioHardwareServiceDeviceProperty_VirtualMasterVolume, inScope, kAudioObjectPropertyElementMaster };
    bool hasVirtualMasterVolume = AudioHardwareServiceHasProperty(inDevice.GetObjectID(), &virtualMasterVolumeAddress);
    
    Boolean virtualMasterVolumeIsSettable;
    OSStatus err = AudioHardwareServiceIsPropertySettable(inDevice.GetObjectID(), &virtualMasterVolumeAddress, &virtualMasterVolumeIsSettable);
    virtualMasterVolumeIsSettable &= (err == kAudioServicesNoError);
    
    if(hasVirtualMasterVolume && virtualMasterVolumeIsSettable)
    {
        // Not sure why, but setting the virtual master volume sets all channels to the same volume. As a workaround, we store
        // the current balance here so we can reset it after setting the volume.
        Float32 virtualMasterBalance;
        bool didGetVirtualMasterBalance = GetVirtualMasterBalance(inDevice, inScope, virtualMasterBalance);
        
        didSetVolume = kAudioServicesNoError == AHSSetPropertyData(inDevice.GetObjectID(), &virtualMasterVolumeAddress, sizeof(Float32), &inVolume);
        
        // Reset the balance
        AudioObjectPropertyAddress virtualMasterBalanceAddress =
            { kAudioHardwareServiceDeviceProperty_VirtualMasterBalance, inScope, kAudioObjectPropertyElementMaster };
        
        if(didSetVolume && didGetVirtualMasterBalance && AudioHardwareServiceHasProperty(inDevice.GetObjectID(), &virtualMasterBalanceAddress))
        {
            Boolean balanceIsSettable;
            err = AudioHardwareServiceIsPropertySettable(inDevice.GetObjectID(), &virtualMasterBalanceAddress, &balanceIsSettable);
            if(err == kAudioServicesNoError && balanceIsSettable)
            {
                AHSSetPropertyData(inDevice.GetObjectID(), &virtualMasterBalanceAddress, sizeof(Float32), &virtualMasterBalance);
            }
        }
    }
    
    return didSetVolume;
}
 bool canSetVolume()
 {
     Boolean isSettable = NO;
     return AudioHardwareServiceIsPropertySettable (outputDeviceID, &addr, &isSettable) == noErr
              && isSettable;
 }
Beispiel #3
0
// setting system volume. Mutes if under threshhold.
// Courtesy: https://gist.github.com/atr000/205140
int setVolume(float newVolume) {
  if (newVolume < 0.0 || newVolume > 1.0) {
    fprintf(stderr, "ERROR: Requested volume out of range (%.2f)\n", newVolume);
    return 1;
  }

  // get output device
  UInt32 propertySize = 0;
  OSStatus status = noErr;
  AudioObjectPropertyAddress propertyAOPA;
  propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
  propertyAOPA.mScope = kAudioDevicePropertyScopeOutput;

  if (newVolume < 0.001) {
    propertyAOPA.mSelector = kAudioDevicePropertyMute;
  } else {
    propertyAOPA.mSelector = kAudioHardwareServiceDeviceProperty_VirtualMasterVolume;
  }

  AudioDeviceID outputDeviceID = defaultOutputDeviceID();

  if (outputDeviceID == kAudioObjectUnknown) {
    fprintf(stderr, "ERROR: Unknown device\n");
    return 2;
  }

  if (!AudioHardwareServiceHasProperty(outputDeviceID, &propertyAOPA)) {
    fprintf(stderr, "ERROR: Device 0x%0x does not support volume control\n", outputDeviceID);
    return 3;
  }

  Boolean canSetVolume = false;
  status = AudioHardwareServiceIsPropertySettable(outputDeviceID, &propertyAOPA, &canSetVolume);

  if (status || !canSetVolume) {
    fprintf(stderr, "ERROR: Device 0x%0x does not support volume control\n", outputDeviceID);
    return 4;
  }

  if (propertyAOPA.mSelector == kAudioDevicePropertyMute) {
    propertySize = sizeof(UInt32);
    UInt32 mute = 1;
    status = AudioHardwareServiceSetPropertyData(outputDeviceID, &propertyAOPA, 0, NULL, propertySize, &mute);
  } else {
    propertySize = sizeof(Float32);
    status = AudioHardwareServiceSetPropertyData(outputDeviceID, &propertyAOPA, 0, NULL, propertySize, &newVolume);

    if (status) {
      fprintf(stderr, "ERROR: Unable to set volume for device 0x%0x\n", outputDeviceID);
      return 5;
    }

    // make sure we're not muted
    propertyAOPA.mSelector = kAudioDevicePropertyMute;
    propertySize = sizeof(UInt32);
    UInt32 mute = 0;

    if (!AudioHardwareServiceHasProperty(outputDeviceID, &propertyAOPA)) {
      fprintf(stderr, "ERROR: Device 0x%0x does not support muting\n", outputDeviceID);
      return 6;
    }

    Boolean canSetMute = false;

    status = AudioHardwareServiceIsPropertySettable(outputDeviceID, &propertyAOPA, &canSetMute);

    if (status || !canSetMute) {
      fprintf(stderr, "ERROR: Device 0x%0x does not support muting\n", outputDeviceID);
      return 7;
    }

    status = AudioHardwareServiceSetPropertyData(outputDeviceID, &propertyAOPA, 0, NULL, propertySize, &mute);
  }

  if (status) {
    fprintf(stderr, "ERROR: Unable to set volume for device 0x%0x\n", outputDeviceID);
    return 8;
  }

  return 0;
}