"use strict"; // // audioDeviceExample.js // examples // // Created by Brad Hefta-Gaub on 3/22/14 // Copyright 2013 High Fidelity, Inc. // // This is an example script that demonstrates use of the Menu object // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // (function() { // BEGIN LOCAL_SCOPE const INPUT = "Input"; const OUTPUT = "Output"; function parseMenuItem(item) { const USE = "Use "; const FOR_INPUT = " for " + INPUT; const FOR_OUTPUT = " for " + OUTPUT; if (item.slice(0, USE.length) == USE) { if (item.slice(-FOR_INPUT.length) == FOR_INPUT) { return { device: item.slice(USE.length, -FOR_INPUT.length), mode: INPUT }; } else if (item.slice(-FOR_OUTPUT.length) == FOR_OUTPUT) { return { device: item.slice(USE.length, -FOR_OUTPUT.length), mode: OUTPUT }; } } } // // VAR DEFINITIONS // var debugPrintStatements = true; const INPUT_DEVICE_SETTING = "audio_input_device"; const OUTPUT_DEVICE_SETTING = "audio_output_device"; var audioDevicesList = []; var wasHmdActive = false; // assume it's not active to start var switchedAudioInputToHMD = false; var switchedAudioOutputToHMD = false; var previousSelectedInputAudioDevice = ""; var previousSelectedOutputAudioDevice = ""; var skipMenuEvents = true; // // BEGIN FUNCTION DEFINITIONS // function debug() { if (debugPrintStatements) { print.apply(null, [].concat.apply(["selectAudioDevice.js:"], [].map.call(arguments, JSON.stringify))); } } function setupAudioMenus() { // menu events can be triggered asynchronously; skip them for 200ms to avoid recursion and false switches skipMenuEvents = true; Script.setTimeout(function() { skipMenuEvents = false; }, 200); removeAudioMenus(); // Setup audio input devices Menu.addSeparator("Audio", "Input Audio Device"); var inputDevices = AudioDevice.getInputDevices(); for (var i = 0; i < inputDevices.length; i++) { var audioDeviceMenuString = "Use " + inputDevices[i] + " for Input"; Menu.addMenuItem({ menuName: "Audio", menuItemName: audioDeviceMenuString, isCheckable: true, isChecked: inputDevices[i] == AudioDevice.getInputDevice() }); audioDevicesList.push(audioDeviceMenuString); } // Setup audio output devices Menu.addSeparator("Audio", "Output Audio Device"); var outputDevices = AudioDevice.getOutputDevices(); for (var i = 0; i < outputDevices.length; i++) { var audioDeviceMenuString = "Use " + outputDevices[i] + " for Output"; Menu.addMenuItem({ menuName: "Audio", menuItemName: audioDeviceMenuString, isCheckable: true, isChecked: outputDevices[i] == AudioDevice.getOutputDevice() }); audioDevicesList.push(audioDeviceMenuString); } } function checkDeviceMismatch() { var inputDeviceSetting = Settings.getValue(INPUT_DEVICE_SETTING); var interfaceInputDevice = AudioDevice.getInputDevice(); if (interfaceInputDevice != inputDeviceSetting) { debug("Input Setting & Device mismatch! Input SETTING: " + inputDeviceSetting + "Input DEVICE IN USE: " + interfaceInputDevice); switchAudioDevice("Use " + inputDeviceSetting + " for Input"); } var outputDeviceSetting = Settings.getValue(OUTPUT_DEVICE_SETTING); var interfaceOutputDevice = AudioDevice.getOutputDevice(); if (interfaceOutputDevice != outputDeviceSetting) { debug("Output Setting & Device mismatch! Output SETTING: " + outputDeviceSetting + "Output DEVICE IN USE: " + interfaceOutputDevice); switchAudioDevice("Use " + outputDeviceSetting + " for Output"); } } function removeAudioMenus() { Menu.removeSeparator("Audio", "Input Audio Device"); Menu.removeSeparator("Audio", "Output Audio Device"); for (var index = 0; index < audioDevicesList.length; index++) { if (Menu.menuItemExists("Audio", audioDevicesList[index])) { Menu.removeMenuItem("Audio", audioDevicesList[index]); } } Menu.removeMenu("Audio > Devices"); audioDevicesList = []; } function onDevicechanged() { debug("System audio devices changed. Removing and replacing Audio Menus..."); setupAudioMenus(); checkDeviceMismatch(); } function onMenuEvent(audioDeviceMenuString) { if (!skipMenuEvents) { switchAudioDevice(audioDeviceMenuString); } } function switchAudioDevice(audioDeviceMenuString) { // if the device is not plugged in, short-circuit if (!~audioDevicesList.indexOf(audioDeviceMenuString)) { return; } var selection = parseMenuItem(audioDeviceMenuString); if (!selection) { debug("Invalid Audio audioDeviceMenuString! Doesn't end with 'for Input' or 'for Output'"); return; } // menu events can be triggered asynchronously; skip them for 200ms to avoid recursion and false switches skipMenuEvents = true; Script.setTimeout(function() { skipMenuEvents = false; }, 200); var selectedDevice = selection.device; if (selection.mode == INPUT) { var currentInputDevice = AudioDevice.getInputDevice(); if (selectedDevice != currentInputDevice) { debug("Switching audio INPUT device from " + currentInputDevice + " to " + selectedDevice); Menu.setIsOptionChecked("Use " + currentInputDevice + " for Input", false); if (AudioDevice.setInputDevice(selectedDevice)) { Settings.setValue(INPUT_DEVICE_SETTING, selectedDevice); Menu.setIsOptionChecked(audioDeviceMenuString, true); } else { debug("Error setting audio input device!") Menu.setIsOptionChecked(audioDeviceMenuString, false); } } else { debug("Selected input device is the same as the current input device!") Settings.setValue(INPUT_DEVICE_SETTING, selectedDevice); Menu.setIsOptionChecked(audioDeviceMenuString, true); AudioDevice.setInputDevice(selectedDevice); // Still try to force-set the device (in case the user's trying to forcefully debug an issue) } } else if (selection.mode == OUTPUT) { var currentOutputDevice = AudioDevice.getOutputDevice(); if (selectedDevice != currentOutputDevice) { debug("Switching audio OUTPUT device from " + currentOutputDevice + " to " + selectedDevice); Menu.setIsOptionChecked("Use " + currentOutputDevice + " for Output", false); if (AudioDevice.setOutputDevice(selectedDevice)) { Settings.setValue(OUTPUT_DEVICE_SETTING, selectedDevice); Menu.setIsOptionChecked(audioDeviceMenuString, true); } else { debug("Error setting audio output device!") Menu.setIsOptionChecked(audioDeviceMenuString, false); } } else { debug("Selected output device is the same as the current output device!") Settings.setValue(OUTPUT_DEVICE_SETTING, selectedDevice); Menu.setIsOptionChecked(audioDeviceMenuString, true); AudioDevice.setOutputDevice(selectedDevice); // Still try to force-set the device (in case the user's trying to forcefully debug an issue) } } } function restoreAudio() { if (switchedAudioInputToHMD) { debug("Switching back from HMD preferred audio input to: " + previousSelectedInputAudioDevice); switchAudioDevice("Use " + previousSelectedInputAudioDevice + " for Input"); switchedAudioInputToHMD = false; } if (switchedAudioOutputToHMD) { debug("Switching back from HMD preferred audio output to: " + previousSelectedOutputAudioDevice); switchAudioDevice("Use " + previousSelectedOutputAudioDevice + " for Output"); switchedAudioOutputToHMD = false; } } // Some HMDs (like Oculus CV1) have a built in audio device. If they // do, then this function will handle switching to that device automatically // when you goActive with the HMD active. function checkHMDAudio() { // HMD Active state is changing; handle switching if (HMD.active != wasHmdActive) { debug("HMD Active state changed!"); // We're putting the HMD on; switch to those devices if (HMD.active) { debug("HMD is now Active."); var hmdPreferredAudioInput = HMD.preferredAudioInput(); var hmdPreferredAudioOutput = HMD.preferredAudioOutput(); debug("hmdPreferredAudioInput: " + hmdPreferredAudioInput); debug("hmdPreferredAudioOutput: " + hmdPreferredAudioOutput); if (hmdPreferredAudioInput !== "") { debug("HMD has preferred audio input device."); previousSelectedInputAudioDevice = Settings.getValue(INPUT_DEVICE_SETTING); debug("previousSelectedInputAudioDevice: " + previousSelectedInputAudioDevice); if (hmdPreferredAudioInput != previousSelectedInputAudioDevice) { switchedAudioInputToHMD = true; switchAudioDevice("Use " + hmdPreferredAudioInput + " for Input"); } } if (hmdPreferredAudioOutput !== "") { debug("HMD has preferred audio output device."); previousSelectedOutputAudioDevice = Settings.getValue(OUTPUT_DEVICE_SETTING); debug("previousSelectedOutputAudioDevice: " + previousSelectedOutputAudioDevice); if (hmdPreferredAudioOutput != previousSelectedOutputAudioDevice) { switchedAudioOutputToHMD = true; switchAudioDevice("Use " + hmdPreferredAudioOutput + " for Output"); } } } else { debug("HMD no longer active. Restoring audio I/O devices..."); restoreAudio(); } } wasHmdActive = HMD.active; } // // END FUNCTION DEFINITIONS // // // BEGIN SCRIPT BODY // // Wait for the C++ systems to fire up before trying to do anything with audio devices Script.setTimeout(function () { debug("Connecting deviceChanged(), displayModeChanged(), and switchAudioDevice()..."); AudioDevice.deviceChanged.connect(onDevicechanged); HMD.displayModeChanged.connect(checkHMDAudio); Menu.menuItemEvent.connect(onMenuEvent); debug("Setting up Audio I/O menu for the first time..."); setupAudioMenus(); checkDeviceMismatch(); debug("Checking HMD audio status...") checkHMDAudio(); }, 3000); debug("Connecting scriptEnding()"); Script.scriptEnding.connect(function () { restoreAudio(); removeAudioMenus(); Menu.menuItemEvent.disconnect(onMenuEvent); HMD.displayModeChanged.disconnect(checkHMDAudio); AudioDevice.deviceChanged.disconnect(onDevicechanged); }); // // END SCRIPT BODY // }()); // END LOCAL_SCOPE