When the standard keyboard key to increase the volume is hit on windows, a small window appears in the upper left displaying the volume and possibly information about playing media. I am looking for a way to trigger the window without changing the volume status, preferably in an easy to integrate way with Autohotkey.
Windows 8 introduced the MediaControl class that lets Modern apps hook into the system playback control. In Windows 8.1 and 10 it was replaced by the SystemMediaTransportControls class.
While it supports "Manual control of the System Media Transport Controls" there does not seem to be a way to show/hide the overlay and certainly not from a desktop app.
Going into undocumented territory I found the class name of the overlay and that lead me to HideVolumeOSD. Unfortunately the class names are rather generic so you probably have to look at the size of the window as well to determine if it is the volume overlay.
I don't know if just showing the window will work, Windows is not expecting it to be visible except in response to keyboard and playback events. The HideVolumeOSD app uses keybd_event (volume up/down) to trigger it but this is problematic as noted in the comments...
I set up an autohotkey to send volume up, the volume down (For shortcut ctrl+pgdn).
^PgDn::
Send {Volume_Up}
Send {Volume_Down}
return
The desired behavior when used with volume control keys can be gotten by storing the current volume, sending a media key press, then updating the volume to the stored value +/- 1 (or +0, if just seeking to display the OSD).
There is an edge case when mimicking Volume_Down when the volume is <=3. The sent Volume_Down keypress triggers the mute, but this can be accommodated for by manually setting the mute to be off afterwards, or by pre-setting the volume to 4, which prevents the rounding to 0. Which method to choose depends upon if you prefer a brief blip of potentially louder sound (pre-set to 4) or a brief mute (disable mute afterwards) when the volume is lowered from <=3 values.
The following code is in AHK v2.
; Prefix with '$' so as to prevent self triggering, since internally sending Volume_Up
$Volume_Up::{
; Get the current volume. SoundGetVolume returns a float which often needs rounding
volume:=Round(SoundGetVolume())
; Send the Volume_Up key, so the media display is triggered
Send "{Volume_Up}"
; Indiscriminately set the volume manually to volume+1
SoundSetVolume(volume+1)
}
; Much the same as above, yet when sending Volume_Down at volumes <=3 the volume
; is rounded down to 0 which triggers mute. The volume is then set properly,
; yet remains muted. In order to not hear a cut in the audio, you need to set
; the volume to 4 when (1<volume<=3) so that the Volume_Down doesn't round it
; down to 0, or disable the mute afterwards (if volume > 1). This causes a
; brief volume spike or mute blip, respectively. Currently the volume spike option
; is uncommented.
$Volume_Down::{
volume:=Round(SoundGetVolume())
; Bumping the volume before sending the Volume_Down to prevent mute blip (if needed)
if(1 < volume and volume <= 3){
SoundSetVolume(4)
}
Send "{Volume_Down}"
SoundSetVolume(volume-1)
; ; Disable accidental triggering of mute when volume >= 3 after sending Volume_Down
; if(volume > 1) {
; SoundSetMute(0)
; }
}
To just trigger the OSD as the question asks the following works. Hitting a volume key then quickly resetting the volume will displays it, yet considerations need to be made if currently muted as to prevent a blip of sound. Volume keys are used since double toggling Volume_Mute causes a gap in the sound output.
; Trigger the on screen display of the volume bar by hitting Volume_Up and
; then quickly resetting the volume. If muted and send Volume_Up, we will
; hear audio at original volume for a brief second. To prevent this, we
; can set the volume to 0 and send Volume_Down instead.
^PgUp::{
volume:=Round(SoundGetVolume())
muted:=SoundGetMute()
; Trigger the display with a volume key (might briefly bump the volume if unmuted)
if (muted or volume == 0) {
SoundSetVolume(0)
Send "{Volume_Down}"
} else {
Send "{Volume_Up}"
}
; Reset to the original volume and mute status
SoundSetMute(muted)
SoundSetVolume(volume)
}
All of the same code, yet uncommented:
$Volume_Up::{
volume:=Round(SoundGetVolume())
Send "{Volume_Up}"
SoundSetVolume(volume+1)
}
$Volume_Down::{
volume:=Round(SoundGetVolume())
if(1 < volume and volume <= 3){
SoundSetVolume(4)
}
Send "{Volume_Down}"
SoundSetVolume(volume-1)
}
^PgUp::{
volume:=Round(SoundGetVolume())
muted:=SoundGetMute()
if (muted or volume == 0) {
SoundSetVolume(0)
Send "{Volume_Down}"
} else {
Send "{Volume_Up}"
}
SoundSetMute(muted)
SoundSetVolume(volume)
}
Building on Anna Wang's answer (https://stackoverflow.com/a/62012058/3251466).
This will restore the volume even when it was at an odd value.
^PgDn:: ;Ctrl+Page Down
SoundGet, original_volume
SendInput {Volume_Down}
SoundSet, original_volume
return
Related
In an app, I'm driving a laser projection device using a connected USB audio interface on macOS.
The laser device takes analog audio as an input.
As a safety feature, it would be great if I could make the audio output from my app the exclusive output, because any other audio from other apps or from the OS itself which is routed to the USB audio interface is mixed with my laser control audio, is unwanted and a potential safety hazard.
Is it possible on macOS to make my app's audio output exclusive? I know you can configure AVAudioSession on iOS to achieve this (somewhat - you can duck other apps' audio, but notification sounds will in turn duck your app), but is something like this possible on the Mac? It does not need to be AppStore compatible.
Yes, you can request that CoreAudio gives you exclusive access to an audio output device. This is called hogging the device. If you hogged all of the devices, no other application (including the system) would be able to emit any sound.
Something like this would do the trick for a single device:
AudioObjectPropertyAddress HOG_MODE_PROPERTY = { kAudioDevicePropertyHogMode, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
AudioDeviceID deviceId = // your audio device ID
pid_t hoggingProcess = -1; // -1 means attempt to acquire exclusive access
UInt32 size = sizeof(pid_t);
AudioObjectSetPropertyData(deviceId, &HOG_MODE_PROPERTY, 0, NULL, size, &hoggingProcess);
assert(hoggingProcess == getpid()); // check that you have exclusive access
Hog mode works by setting an AudioObject property called kAudioDevicePropertyHogMode. The value of the property is -1 if the device is not hogged. If it is hogged the value is the process id of the hogging process.
If you jump to definition on kAudioDevicePropertyHogMode in Xcode you can read the header doc for the hog mode property. That is the best way to learn about how this property (and pretty much anything and everything else in CoreAudio) works.
For completeness, here's the header doc:
A pid_t indicating the process that currently owns exclusive access to the
AudioDevice or a value of -1 indicating that the device is currently
available to all processes. If the AudioDevice is in a non-mixable mode,
the HAL will automatically take hog mode on behalf of the first process to
start an IOProc.
Note that when setting this property, the value passed in is ignored. If
another process owns exclusive access, that remains unchanged. If the
current process owns exclusive access, it is released and made available to
all processes again. If no process has exclusive access (meaning the current
value is -1), this process gains ownership of exclusive access. On return,
the pid_t pointed to by inPropertyData will contain the new value of the
property.
I wrote an FMX (FireMonkey) application and I want to change (increase / decrease) and mute / unmute the master volume output in OS X. Either in Delphi or C++Builder. Alternatively I would do it by simulating key presses of the specific keys of the keyboard.
For Windows, it is fairly easily by simulating key presses with SendInput() or even easier with keybd_event().
This is how it works on windows for me:
// vkVolumeUp / vkVolumeDown / vkVolumeMute
// VK_VOLUME_UP / VK_VOLUME_DOWN / VK_VOLUME_MUTE
keybd_event(vkVolumeUp, 1, 0, 0);
keybd_event(vkVolumeUp, 1, KEYEVENTF_KEYUP, 0);
But I can't manage to compile it for OS X, since the IDE tells me that it doesn't know this functions. A direct way to change the volume would be even better if it is possible.
This is probably a long-winded, inefficient way of doing it, but you can mute the volume from the Terminal like this:
osascript -e 'set volume with output muted'
and increase it by 20 notches like this
osascript -e 'set volume output volume ((output volume of (get volume settings)) + 20)'
I presume you could use the system() command to execute those till someone tells you a better way.
I was writing some pices in winapi's raw input
It seem to working though I am not sure how reliable (unfaliable) it is
(and if it will be working on all systems machines etc, this is a bit worry)
also there appears many question, the one is
I would like to use my first (I mean normal/base mouse) in old way,
it is processint WM_MOUSEMOVE etc and moving arrow cursor, only the
secondary mouse I need processing by raw_input (primary can stay untouched by rawinput), the problem is
1) how can i be sure which mouse detected by rawinput is the
secondary?
2) the second mouse moves also my arrow -cursor, if I disable
it by RIDEV_NOLEGACY then both are not moving cursor (it bacame hourglass) and it is wrong too
think maybe i should setup it a bit differently my setrup rawinput function is like
void SetupRawInput()
{
static RAWINPUTDEVICE Rid[1];
Rid[0].usUsagePage = 0x01;
Rid[0].usUsage = 0x02;
Rid[0].dwFlags = 0; // Rid[0].dwFlags = RIDEV_NOLEGACY; /
Rid[0].hwndTarget = NULL;
int r = RegisterRawInputDevices( Rid, 1, sizeof(Rid[0]) );
if (!r) ERROR_EXIT("raw input register fail");
}
how to resolve this issueas andmake it work? tnx
I don't know if my approach is the best one, or not, but this is how I do it for the first item in your question:
When I process WM_INPUT using GetRawInputData(...), I check to see if the device handle passed back by the RAWINPUTHEADER structure (contained within the RAWINPUT structure returned from the function) is the same as the device I want to use. If it is not, then I simply don't bother sending back data, if it is, I then process the RAWINPUTMOUSE data returned in the RAWINPUT struct.
And if you're wondering how to get the list of devices, you can use GetRawInputDeviceList(...), which will return the device handles of the mice you're trying to work with.
As I said, this may not be the best way, but I have confirmed that it does work for my purposes. I also do this for my keyboard raw input data as well.
As for item #2, it seems likely that it affects both mice because Windows has exclusive access to the mice, so you can't register one specific mouse without registering them all with the same flags. But someone with more knowledge than I could probably give a better explanation.
I would like to remap the Mac Eject Key into Insert, in particular for emulating Ctrl+Insert, Alt+Insert, Shift+Insert and other common key combinations in Windows applications.
Which is the Virtual Key code for the Eject Key? I found some virtual key tables, but for some reason the Eject Key is never included.
Q: Which is the Virtual Key in Mac for the Eject Key?
A: None.
With reference to HID Usage Tables for Universal Serial Bus, Eject is not a keypress but actually a HID Usage - a One Shot Control from the Consumer Usage Page.
3.4.1.4 One Shot Control (OSC)
A One Shot Control is a push button that triggers a single event or action. A One Shot Control is encoded into a 1-bit
value and declared as a Relative, Preferred, Main item with a Logical Minimum and Logical Maximum of 0 and 1,
respectively. A 0 to 1 transition initiates an event. Nothing occurs on a 1 to 0 transition but it is required before another
event can occur. An example is degauss.
On the Consumer Usage Page (0x0C) the Eject Usage ID is defined as:
Usage ID
Usage Name
Usage Types
Section
B8
Eject
OSC
15.7
If you were wanting to fake an Eject keypress from a USB HID-capable Arduino, such as the Leonardo, you could do so with the following code which sends a Control-Shift-Eject to lock the screen...
// NicoHood's HID-Project
#include "HID-Project.h"
void setup() {
// Make pin 2 an input and turn on the pull-up resistor
// so it goes high unless connected to ground:
pinMode(2, INPUT_PULLUP);
Keyboard.begin();
// Sends a clean report to the host.
// This is important on any Arduino type.
Consumer.begin();
}
void loop() {
// Control-Shift-Eject locks the screen
if (digitalRead(2) == LOW) {
Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press(KEY_LEFT_SHIFT);
// This is a One Shot Command so doesn't need a "release"
Consumer.write(HID_CONSUMER_EJECT);
// Debounce
delay(100);
while (digitalRead(2) == LOW)
delay(100);
// Release Control-Shift keys
Keyboard.releaseAll();
}
}
As to reacting to an Eject keypress from Windows, unless it's raised as one of the Media Control-related WM_ events I expect you'll have to write a USB HID/ACPI driver that detects and raises the OSC itself.
There is Sample buttons in ACPI for device running Windows 10 desktop editions which demonstrates capturing some of the other OSCs from the Consumer page (such as Volume Increments/Decrements), you could probably expand upon this to include Eject.
Sorry I can't be of more help there, but hopefully this points you in the right direction.
I know that question has been made years ago, but maybe some people are still looking for that answer and will end up finding that question so...
Line 1805 of VoodooPS2Keyboard.cpp from VoodooPS2Controller by RehabMan says that the virtual key on Mac for the Eject Key is 0x92.
Reference:
https://github.com/RehabMan/OS-X-Voodoo-PS2-Controller/blob/master/VoodooPS2Keyboard/VoodooPS2Keyboard.cpp
I have to dash away from the computer frequently, and I want to trigger some commands to run when my iPhone is close enough/far enough from my iMac (next to it vs. 2-3 metres away/other side of a wall). A couple of minutes latency is fine.
Partial solution: proximity
I've downloaded reduxcomputing-proximity and it works, but this only triggers when the device goes in to/out of range of bluetooth, but my desired range is much smaller.
(Proximity polls [IOBluetoothDevice -remoteNameRequest] to see if the device is in bluetooth range or not.)
Enhancement: rawRSSI
I've used [IOBluetoothDevice -rawRSSI] to get the RSSI when I am connected to the iPhone (when disconnected this just returns +127), but in order to save the battery life of my iPhone I'd rather avoid establishing a full bluetooth connection.
Am I correct in thinking that maintaining a connection will consume more battery life than just polling every couple of minutes?
I've overridden the isInRange method of proximity here to give me a working solution that's probably relatively battery intensive compared to the previous remoteNameRequest: method:
- (BOOL)isInRange {
BluetoothHCIRSSIValue RSSI = 127; /* Valid Range: -127 to +20 */
if (device) {
if (![device isConnected]) {
[device openConnection];
}
if ([device isConnected]) {
RSSI = [device rawRSSI];
[device closeConnection];
}
}
return (RSSI >= -60 && RSSI <= 20);
}
(Proximity uses synchronous calls - if and when I fit it to my needs I will edit it to be asynchronous but for now that's not important.)
Under Linux: l2ping - inquiry scan?
This SO post references getting an RSSI during an 'inquiry scan' which sounds like what I want, but it talks about using the Linux Bluez library, whilst I am on a Mac - I'd rather do it without having to stray too far if possible! (I have considered using a VM with USB pass-thru to hook up a second bluetooth device... But a simpler solution would be preferable!)
I see there is a IOBluetoothDeviceInquiry class, but I am not sure if this is useful to me. I don't intend to learn bluetooth protocol just for this simple problem!
The commands
For interest, and not particularly relevant to the solution, here are the Apple Scripts I currently trigger when
in range:
tell application "Skype"
send command "SET USERSTATUS ONLINE" script name "X"
do shell script "afplay '/System/Library/Sounds/Blow.aiff'"
end tell
out of range:
tell application "Skype"
send command "SET USERSTATUS AWAY" script name "X"
do shell script "afplay '/System/Library/Sounds/Basso.aiff'"
end tell
Though these are likely to get longer!
You are correct that making a connection will cost more energy. However, I'm not aware of APIs on mac OS that will give you access to the RSSI from inquiry scan packets. You could get access to the raw packets from your BT adapter using Mac OS PacketLogger. See this post Bluetooth sniffer - preferably mac osx
You could programmaticly put your device in discovery every couple of minutes, capture the inquiry scan packets with the packetlogger, and parse out the RSSI. You can use WireShark to help you understand how to decode the packets and find RSSI.
Your simplest option is probably to just periodically create a connection, measure RSSI, and then tear down the connection.
In terms of tradeoffs for your use case doing a continuous or periodic inquiry will consume same or even a bit more energy as doing a periodic connect / read RSSI and disconnect. Depending on the use case it sometimes may be more efficient to maintain the connection in a low power mode (sniff with 2.56 sec interval) and remain connected if the device is in range. And use RSSI to monitor proximity (although it is not accurate as interference due to objects change rssi drastically even though the device might be in proximity)