How to ignore changing audio-output from System Preference? (macOS) - macos

I made my app can select audio-output. (like 'system default' or 'user's DAC')
but when user choose a output from system preferences panel - sound, my app's output follows the output user seleced.
I searched a lot and add some listener so I can change immediatly my app's output to previously user selected if system output has been changed.
BUT it makes very anonying few milliseconds swiching delay.
I guess it is because I switch my app's output after it's already changed to system default.
So I wonder If I can know BEFORE system default output's changing.
(Like viewWillAppear api from cocoa)
Thank you.
listener that I used for knowing chaninging of system default audio out is from the article below.
How to get notification if System Preferences Default Sound changed
thanks
more details
I used AudioUnitSetProperty(audioOut, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Output, 0, &deviceID, (UInt32)sizeof(deviceID)) for selecting output device. apple document
and add this listener
func addListenerBlock(listenerBlock: #escaping AudioObjectPropertyListenerBlock, onAudioObjectID: AudioObjectID, forPropertyAddress: inout AudioObjectPropertyAddress) {
if (kAudioHardwareNoError != AudioObjectAddPropertyListenerBlock(onAudioObjectID, &forPropertyAddress, nil, listenerBlock)) {
LOG("Error calling: AudioObjectAddPropertyListenerBlock") }
}
func add() {
var propertyAddress = AudioObjectPropertyAddress(mSelector: kAudioHardwarePropertyDefaultOutputDevice,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster)
self.addListenerBlock(listenerBlock: audioObjectPropertyListenerBlock,
onAudioObjectID: AudioObjectID(bitPattern: kAudioObjectSystemObject),
forPropertyAddress: &propertyAddress)
}

kAudioUnitSubType_DefaultOutput tracks the current output device selected by the user in the Sound Preferences. To play to a specific device use kAudioUnitSubType_HALOutput. The comments in AUComponent.h are helpful:
#enum Apple input/output audio unit sub types (OS X)
#constant kAudioUnitSubType_HALOutput
- desktop only
The audio unit that interfaces to any audio device. The user specifies which
audio device to track. The audio unit can do input from the device as well as
output to the device. Bus 0 is used for the output side, bus 1 is used
to get audio input from the device.
#constant kAudioUnitSubType_DefaultOutput
- desktop only
A specialisation of AUHAL that is used to track the user's selection of the
default device as set in the Sound Prefs
#constant kAudioUnitSubType_SystemOutput
- desktop only
A specialisation of AUHAL that is used to track the user's selection of the
device to use for sound effects, alerts
and other UI sounds.
You didn't specify how you're setting up your output (AUGraph?) so the way to use kAudioUnitSubType_HALOutput varies.

Related

volume buttons have no effect when playing through ear piece

In my Xamarin Forms Android app I'm sending audio through the ear piece instead of the normal speaker. I'm doing something like:
myMediaPlayer.SetAudioAttributes(new AudioAttributes.Builder().SetLegacyStreamType(Stream.VoiceCall).Build());
The deprecated version of the above is
myMediaPlayer.SetAudioStreamType(Stream.VoiceCall);
Either way, this seems to work (though, if there is a better way, please let me know).
HOWEVER, I cannot control the volume. The sound correctly comes out of the ear speaker only, but it is at a constant volume, ignoring my volume key presses (though the volume meter displays on the screen going up and down as a press the buttons). ... (for clarification, I'm not trying to control the volume programatically, I simply want the device to adjust the volume as one would expect.)
Any help?
UPDATE
I've also tried this code:
myMediaPlayer.SetAudioAttributes(new AudioAttributes.Builder().SetContentType(AudioContentType.Music).SetUsage(AudioUsageKind.VoiceCommunication).Build());
I have two goals:
Play only out of the ear piece (or headphones if attached)
Be able to control the volume (which I thought would be a given)
UPDATE with Code Sample
The following will play through the earpiece, but not change volume.
public void Play(string url)
{
var myMediaPlayer = new MediaPlayer();
myMediaPlayer.SetDataSource(url);
if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
myMediaPlayer.SetAudioAttributes(new AudioAttributes.Builder().SetContentType(AudioContentType.Music).SetUsage(AudioUsageKind.VoiceCommunication).Build());
else
myMediaPlayer.SetAudioStreamType(Stream.VoiceCall);
myMediaPlayer.Prepare();
myMediaPlayer.Start();
}

Change the output device for AVAudioPlayer on OSX

I need to play audio from AVAudioPlayer to a particular audio device such as a USB headset. By default the audio will get played out to the system default device setting. With QTKit, I was able to use SetMovieAudioContext() to do such a task but do not see equivalent in AVFoundation. How is it possible to change this on OSX?
Use NSSound instead:
Get list of audio devices with CoreAudio functions. Then get the devices' unique identifiers:
//...
address.mSelector = kAudioDevicePropertyDeviceUID;
CFStringRef uid = NULL;
UInt32 size = sizeof(uid);
AudioObjectGetPropertyData(deviceId, &address, 0, NULL, &size, &uid);
//...
Initialize sound item, set playbackDeviceIdentifier property value.
Opened a DTS case with Apple and they said it was not possible (as i need to support 10.8). If you only need to support 10.9 and above AVPlayerAudioDeviceSupport should work.

How can I be notified that a movie has been determined not to be a movie?

I'm writing a Mac app that plays MPEG-4 movies using AVPlayer.
My app supports both local movies and movies from the internet, via an “Open Location” dialog. In the latter case, when the user enters a URL and presses the OK button, my app opens the URL as a document.
Local files are easy—Launch Services will tell Finder, Dock, etc. not to light my app up for any local file that isn't a .mp4 or similar. If the user forces my app to open some random file (e.g., by holding down ⌘ when dragging it onto my app), that's their own fault.
Non-local (e.g., internet) URLs are the problem. If I supply a URL that doesn't lead to an MPEG-4, I need to show an alert and close the document.
Of course, I'm not downloading the URL myself; I just hand it to AVPlayer.
So I need a way to be notified by my AVPlayer that what I've given it is not a movie.
I've tried observing the player's status. That gets set to AVPlayerStatusReadyToPlay.
I've tried observing the player's currentItem's tracks. At least on Lion, for an invalid movie, the item's tracks never changes. (This stands to reason—the only reason it would change would be tracks coming in, which is, by definition, impossible for a non-movie.)
I can't just check tracks when status changes, because that may not be set yet (on Mavericks, status changes before tracks).
So, how can I be notified by my AVPlayer that it has conclusively determined that what the URL refers to is not anything it can play?
It's absolutely not definitive, but in my experience, when the player's status becomes AVPlayerStatusReadyToPlay, you can inspect the playable property of asset of the player's currentItem. If playable is YES, you can assume the URL led to a viable MP4. If not, you can assume it did not.
- (void) observeValueForKeyPath:(NSString*)keyPath
ofObject:(id)object
change:(NSDictionary*)change
context:(void*)context
{
if( [keyPath isEqualToString:#"status"] ) {
if( self.player.status == AVPlayerStatusReadyToPlay &&
self.player.currentItem.asset.playable ) {
[self.playerView.player play];
}
else {
// TODO: show an error dialog here.
[self.window close];
}
}
}
I've code similar to the above in one of my apps, and it seems to work as expected in every case I've tried. I can whip up a test app with it if you like.

How to update the volume indicator provided by the MinimalMediaRouteProvider

When registering the MinimalMediaRouteProvider/MediaRouteButton from Androids Chromecast SDK, we get a standard dialog for connecting to existing Cromecast Devices. Once connected to the device, same dialog also provides a way to set the volume using a draggable seek bar. I am having trouble synchronizing the position of this volume seek bar with the actual volume that is already set in the Chromecast device.
As part of registering the MinimalMediaRouteProvider we provide a com.google.cast.MediaRouteAdapter implementation. The onSetVolume(volume) of this interface is called when the user drags the volume seekBar above. This gives us a god way to update the volume level of the connected chromecast channel by using messageStream.setVolume(volume).
The problem is that once we update the volume, there is no way to tell back the MinimalMediaRouteProvider UI that the volume has changed so it can position itself accordingly - currently it always shows the volume as 0.
What is the proper way to notify the MinimalMediaRouteProvider about the current volume level so it can update its volume UI?
Looking at the MediaRoute sample included with support library 7, there seem to be a way to create MediaRouteDescriptor, update the volume there and thus communicate this back to the MediaRouteProvider, but but it is not very clear how to do this in the content of Chromecast/MinimalMediaRouteProvider.
You can call MediaRouteStateListener.onVolumeChanged() to update the volume seekbar.
I have a more detailed answer here:
https://stackoverflow.com/a/18867128/1334870
To get volume immediately from VideoCastManager:
mVolume = (int) (mCastManager.getVolume() * 100) // cast volume is double
To receive system-based volume changes in the receiver (i.e. the on-screen volume slider pop-up, other connected devices changing the volume), you'll need to add a cast consumer in your controller activity:
private VideoCastConsumerImpl mCastConsumer = new VideoCastConsumerImpl() {
public void onVolumeChanged(double volume, boolean isMute)
{
mVolumeSeekBar.setProgress((int) (volume *100));
}
};
For more details:
Android Sender App Development (Aug '14)

Programmatically switch audio devices on Windows 7

On my Windows 7 PC, I've got a set of speakers, some wireless headphones and a USB web cam. This means that I have two possible audio output devices and 2 possible audio input devices.
I find myself having to switch between them fairly frequently. At the moment this is a manual process: right-click on the speaker icon, choose one of "Playback devices" or "Recording devices", choose the correct device in the list (and there's some "dead" ones in there, too) and then hit "Set Default".
I've looked around, and all I can find are people scripting SendKeys to automate this.
That sucks.
Is there anyway to programmatically switch audio input/output devices, so that I can write a simple tray app/hotkey app to make this easier?
Allegedly undocumented COM-interface IPolicyConfig (kudos to #author EreTIk) allows to do that.
This is a sample implementation.
HRESULT SetDefaultAudioPlaybackDevice(LPCWSTR devID)
{
IPolicyConfigVista *pPolicyConfig;
ERole reserved = eConsole;
HRESULT hr = CoCreateInstance(
__uuidof(CPolicyConfigVistaClient),
NULL,
CLSCTX_ALL,
__uuidof(IPolicyConfigVista),
(LPVOID *)&pPolicyConfig);
if (SUCCEEDED(hr))
{
hr = pPolicyConfig->SetDefaultEndpoint(devID, reserved);
pPolicyConfig->Release();
}
return hr;
}
A string of Device ID needs to be passed to this function. An example of a device id
{0.0.1.00000000}.{d915c7bb-d5d7-4c92-80d9-1a0ee5d954f1}
This device id can be obtained through audio device enumeration.
If you are looking into changing default devices programmatically, then this is impossible by design.
Programatically setting the default playback device (and recording device)
How to change default sound playback device programatically?

Resources