Detecting key presses using Term::ReadKey; and syswrite to audiostream; - perl

I am trying to create a simple terminal music application, lets call it a piano.
The application reads keys from the key board, and outputs a sample to a file handle (pointing to a /dev/audio; which doesn't exist unless piped through padsp...but that's a another story).
The application works...sort of. It reads the keys each time one is pressed, great. But the outputs occur every second key that is read... unless I call the output subroutine twice in each loop. If called only once I get no notes played for the first, but the second key pressed leads to playing of the notes of both the keys pressed). I am sorry if I haven't made this clear. Can someone explain what is happening, please? This is loop concerned in Attempt 2
ReadMode 'cbreak';
while(1){
sleep 1/$refreshRate;
my $key = ReadKey(-1) or next;;
last if ( ord($key)==27); # escape key exits
playNote( ord($key)%96); # have to call play note twice
playNote( ord($key)%96);
}

The sample size ($sps variable) has to be at least 1024 for the sound to play immediately when pressing a key rather than playing both notes when pressing another key. Calling playNote twice works but is a workaround for the problem. The reason is probably because pulseaudio waits until the buffer is full to send the audio to the speakers, and 900 bytes doesn't quite fill the buffer. Pulseaudio probably uses a buffer of size 1024.

Related

ReadKey() doesn't track keystrokes in other windows or instances

I wrote a simple script to track keys being pressed and released as shown below:
$j = 0
while($j -lt 10000){
$PressedKey = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown,IncludeKeyUp")
$PressedKey
continue
}
When I run it and start pressing keys, it works. However when I switch to a different Instance or Window and start pressing keys, it stops giving an output. I assume this is because it stops tracking the keystrokes, can't produce an output, or both. Does anyone know any good alternatives to ReadKey() to how to make it track keystrokes on other Windows and/or Instances
I believe this is PowerShell 7
thx.
The object $Host refers to the current PowerShell console host. Therefore, connecting to the ReadKey() method will only return the current PowerShell host keystrokes, and that is the point of the ReadKey() method, return the current host's keystrokes so that other functions (e.g. PSReadline) can act on them.
If you want to globally respond to keystrokes across applications, I personally recommend AutoHotKey. It is fairly easy to create a script that will detect a key combination and perform an action. It is fast and easy to use and I have dozens of key combinations that I have scripted and use every day.
It is possible to create PowerShell scripts that globally hook into keyboard keystrokes and respond to events, but this is beyond the scope of a SO post.

Mplayer - Change track, play/pause with command line

Mplayer provides shortcuts to change the track and play/pause the music but they only work if the mplayer window has the focus.
Is it possible, for example, to change the track currently played in mplayer with a command line? In that case, I could assign this command to a shortcut and use it even if mplayer does not have the focus.
All I found on google was how to use mplayer to play files or which shortcuts to change track when the window has the focus.
I am actually in the middle of trying to do this exact thing! I am going to share what I have discovered so far in case it helps anyone.
I have not been able to find a way to play/pause tracks. However, I have been able to change tracks, at least in terms of going to the next track.
I have found 2 ways so far: Using /proc/$pid/fd/0 and Named Pipes (FIFOs). Explaining FIFOs is a little harder (I just recently learned of them) so I will do that last.
Modifying STDIN via /proc/$pid/fd/0
We can communicate with a process in a different terminal session by redirecting input to the process' file definitions directory at /proc/$pid/fd/ where $pid is the process' PID.
Firstly, find the process ID using pidof. Make sure mplayer is actually running!
$ pidof mplayer
This will output the PID of mplayer. Using this $PID, we can input what we want to STDIN for mplayer by redirecting it to /proc/$PID/fd/0. (Note, 0 refers to STDIN, 1 to STDOUT, etc.). An example:
$ echo "p" > /proc/$PID/fd/0
This has the effect of changing the tracks for mplayer.
However, even though I used p in my example, I have found that you can echo anything (including nothing) and it will still accomplish the same goal of changing the track no matter what you input.
Named Pipes
Also called FIFOs, named pipes, just like regular pipes (|), redirect output to other processes. The main difference is that these are part of the filesystem and can be used to communicate between different terminal sessions.
To start off, create a new named pipe anywhere in your filesystem. I chose /tmp/.
$ mkfifo /tmp/mp3control.pipe
Then start mplayer redirecting its STDIN to the named pipe.
$ mplayer -playlist brutal_tunez.txt < /tmp/mp3control.pipe
It will look like the process hangs and that's because it is expecting the other side to input something.
Now open up a new terminal and input stuff into the pipe.
$ echo "p" > /tmp/mp3control.pipe
You will hear music start to play from mplayer. Again, just like before, any further inputs to the pipe will just change tracks.
I believe this has something to do with the way mplayer handles STDIN and is not the same thing as entering p to play or pause music.
I will continue looking into this but I would love to hear from someone if I made a mistake somewhere. All in all, this might end up being something where the amount of time spent is not worth it, to be honest. (But we still must!)

programmatically press an enter key after starting .exe file in Matlab

In Matlab I can start external .exe files that sometime have a pop up that requires an enter key pressed. For example:
system('C:\Program Files (x86)\WinZip\WINZIP32.EXE')
will start Winzip, and then in order to use it you need to pass the "buy now" pop up window by pressing enter.
Now my problem is not with winzip, I only gave it as an example (i use winrar anyway :).
How can I programmatically press an enter key in Matlab in such cases ? (I use win 7)
Can an event listener be used to solve that?
EDIT: The java.awt.Robot class indeed works on explorer, but not on any software that has a pop up window with an OK button that needs to be pressed. I don't know why it doesn't work for that. I gave the winzip example because I assume everybody has winzip/winrar installed in their machine. The actual software I have is different and irrelevant for the question.
There is a way using Java from Matlab, specifically the java.awt.Robot class. See here.
Apparently there are two types of programs, regarding the way they work when called from Matlab with system('...'):
For some programs, Matlab waits until the program has finished before running the next statement. This happens for example with WinRAR (at least in my Windows 7 machine).
For other programs this doesn't happen, and Matlab proceeds with the next statement right after the external program has been started. An example of this type is explorer (the standard Windows file explorer).
Now, it is possible to return execution to Matlab immediately even for type 1 programs: just add & at the end of the string passed to system. This is standard in Linux Bash shell, and it also works in Windows, as discussed here.
So, you would proceed as follows:
robot = java.awt.Robot;
command = '"C:\Program Files (x86)\WinRAR\WinRAR"'; %// external program; full path
system([command ' &']); %// note: ' &' at the end
pause(5) %// allow some time for the external program to start
robot.keyPress (java.awt.event.KeyEvent.VK_ENTER); %// press "enter" key
robot.keyRelease (java.awt.event.KeyEvent.VK_ENTER); %// release "enter" key
If your applications are only on Windows platform, you can try using .net objects.
The SendWait method of the SendKeys objects allows to send virtually any key, or key combination, to the application which has the focus, including the "modifier" keys like Alt, Shift, Ctrl etc ...
The first thing to do is to import the .net library, then the full syntax to send the ENTER key would be:
NET.addAssembly('System.Windows.Forms');
System.Windows.Forms.SendKeys.SendWait('{ENTER}'); %// send the key "ENTER"
If you only do it once the full syntax is OK. If you plan to make extensive use of the command, you can help yourself with an anonymous helper function.
A little example with notepad
%% // import the .NET assembly and define helper function
NET.addAssembly('System.Windows.Forms');
sendkey = #(strkey) System.Windows.Forms.SendKeys.SendWait(strkey) ;
%% // prepare a few things to send to the notepad
str1 = 'Hello World' ;
str2 = 'OMG ... my notepad is alive' ;
file2save = [pwd '\SelfSaveTest.txt'] ;
if exist(file2save,'file')==2 ; delete(file2save) ; end %// this is just in case you run the test multiple times.
%% // go for it
%// write a few things, save the file then close it.
system('notepad &') ; %// Start notepad, without matlab waiting for the return value
sendkey(str1) %// send a full string to the notepad
sendkey('{ENTER}'); %// send the {ENTER} key
sendkey(str2) %// send another full string to the notepad
sendkey('{! 3}'); %// note how you can REPEAT a key send instruction
sendkey('%(FA)'); %// Send key combination to open the "save as..." dialog
pause(1) %// little pause to make sure your hard drive is ready before continuing
sendkey(file2save); %// Send the name (full path) of the file to save to the dialog
sendkey('{ENTER}'); %// validate
pause(3) %// just wait a bit so you can see you file is now saved (check the titlebar of the notepad)
sendkey('%(FX)'); %// Bye bye ... close the Notepad
As explained in the Microsoft documentation the SendKeys class may have some timing issues sometimes so if you want to do complex manipulations (like Tab multiple times to change the button you actually want to press), you may have to introduce a pause in your Matlab calls to SendKeys.
Try without first, but don't forget you are managing a process from another without any synchronization between them, so timing all that can require a bit of trial and error before you get it right, at least for complex sequences (simple one should be straightforward).
In my case above for example I am running all my data from an external hard drive with an ECO function which puts it into standby, so when I called the "save as..." dialog, it takes time for it to display because the HDD has to wake up. If I didn't introduce the pause(1), sometimes the file path would be imcomplete (the first part of the path was send before the dialog had the focus).
Also, do not forget the & character when you execute the external program. All credit to Luis Mendo for highlighting it. (I tend to forget how important it is because I use it by default. I only omit it if I have to specifically wait for a return value from the program, otherwise I let it run on its own)
The special characters have a special code. Here are a few:
Shift +
Control (Ctrl) ^
Alt %
Tab {TAB}
Backspace {BACKSPACE}, {BS}, or {BKSP}
Validation {ENTER} or ~ (a tilde)
Ins Or Insert {INSERT} or {INS}
Delete {DELETE} or {DEL}
Text Navigation {HOME} {END} {PGDN} {PGUP}
Arrow Keys {UP} {RIGHT} {DOWN} {LEFT}
Escape {ESC}
Function Keys {F1} ... {F16}
Print Screen {PRTSC}
Break {BREAK}
The full list from Microsoft can be found here
There is a small javascript utility that simulates keystrokes like this on the Windows javascript interpreter.
Just create a js file with following code:
var WshShell = WScript.CreateObject("WScript.Shell");
WshShell.SendKeys(WScript.Arguments(0));
then call it from Matlab after the necessary timeout like this:
system('c:\my\js\file\script.js {Enter}');
Can't test here now, but I think this should work...
If you need to run a console-only program in a context that permits full DOS redirection, you can create a file called, say, CR.txt containing a carriage return and use the '<' notation to pipe the value into the program.
This only works if you can provide all the keyboard input can be recorded in the file. It fails dismally if the input has to vary based on responses.
An alternative is to duplicate the input (and possibly output) stream(s) for the program and then pipe data into and out of the program. This is more robust and can permit dynamic responses to the data, but will also likely require substantial effort to implement a robot user to the application.
Rog-O-Matic is an example of a large application completely controlled by a program that monitors screen output and simulates keyboard input to play an early (1980s) ASCII graphic adventure game.
The other responses will be required for GUI-based applications.
Python package pywinauto can wait any dialog and click buttons automatically. But it's capable for native and some .NET applications only. You may have problems with pressing WPF button (maybe QT button is clickable - not checked), but in such case code like app.DialogTitle.wait('ready').set_focus(); app.DialogTitle.type_keys('{ENTER}') may help. Your case is quite simple and probably some tricks with pywinauto are enough. Is your "app with popup" 64-bit or 32-bit?
wait and wait_not functions have timeout parameter. But if you need precisely listener with potentially infinite loop awaiting popups, good direction is global Windows hooks (pyHook can listen mouse and keybd events, but cannot listen dialog opening). I'll try to find my prototype that can detect new windows. It uses UI Automation API event handlers... and... ops... it requires IronPython. I still don't know how to set UI Automation handler with COM interface from standard CPython.
EDIT (2019, January): new module win32hooks was implemented in pywinauto a while ago. Example of usage is here: examples/hook_and_listen.py.

Detect Keyboard Input Matlab

I have a simple question, although it's harder than it seems; I couldn't find the answer on the interwebs :O
I'm writing a script in Matlab. What I want to do is the following:
When I press the esc key, I want a helpdialogue to pop up, so my script pauses. (So when I press esc, I want to stop the whole script to run so that the car (which im writing the script for) stops driving)
How do I do this? How can I say to Matlab: When I press esc, do this...
Thanks for your time guys!
EDIT: It's no option to implement something which awaits the keypress. Im writing a script for a driving car. It just has to drive around basically, but when I press esc for example, it should stop driving. So the script just has to run, untill I press the esc key; then the script has to pause.
KeyPressFcn is good because it forces you to write event-driven code. Which is generally a good idea! However, if KeyPressFcn doesn't seem right for you, for example if you must keep running in a loop, and you just want to poll whether a key has been pressed, I found this solution buried in the matlab website:
get(gcf,'CurrentCharacter')
Then you could set this property to a blank, and poll it as required.
e.g:
finish=false;
set(gcf,'CurrentCharacter','#'); % set to a dummy character
while ~finish
% do things in loop...
% check for keys
k=get(gcf,'CurrentCharacter');
if k~='#' % has it changed from the dummy character?
set(gcf,'CurrentCharacter','#'); % reset the character
% now process the key as required
if k=='q', finish=true; end
end
end
This worked well for me in 2014b. The downside is that the graphics window needs to be focused to receive the key events.
In a matlab figure you can define a 'KeyPressFcn' that works similar to do what you ask.
If you are in the console you have to work around that matlab is single threaded. Basically you need to halt the program flow to check for key presses.
btw - also when you use 'KeyPressFcn' you will need to make some pauses so that Matlab will check if anything has happened.
btw2 - I should also add during this pauses Matlab will not only read your key presses - but also do some housekeeping such as redrawing its window and stuff.
I frequently ran into similar use cases and typically preferred to react to joystick buttons because of the more convenient interface provided by vrjoystick. However, I recently wrote a library that provides a similar interface for keyboard inputs.
% Pause on ESC
kb = HebiKeyboard();
while true
state = read(kb);
if state.ESC
% PAUSE DRIVING
else
% DRIVE CAR
end
end
It's non-blocking and doesn't require focus on any particular figure.
File Exchange: http://mathworks.com/matlabcentral/fileexchange/61306-hebirobotics-matlabinput
Github: https://github.com/HebiRobotics/MatlabInput
I had a related task once, and i did it with getkey form matlab file exchange.
Basicly you will want to have it listen for ascii 1B (27 decimal)
if getkey does not solve your problem you can still have a look at its code and maybe find the line that will do the trick for you.

AutoHotKey - Key spamming does not work like I want it to

Key Spam does not work so good.
Here is the code:
Loop
{
If GetKeyState("z", "P")
send w
If GetKeyState("g", "P")
send a
If GetKeyState("j", "P")
send d
If GetKeyState("h", "P")
send s
}
but when I hold z or zg together, then the original keys z and zg come all time once or twice between the w or wa output.
What can I write that the keys z+g+h+j never will be seen in the text file only w+a+s+d if I run this script?
Karoline, you are going about it the wrong way. Autohotkey was designed to do just what you really want to do.
As MCL says in his comment, you need only to map your keys to a send function
Put this in your script:
z::send w
g::send a
j::send d
h::send s
Now, when you push z it will type w instead.
Keep in mind that this is global over your entire computer unless you limit it to notepad using IfWinActive. The script will not exit on its own.
but this doenst work like i want
i want that i can press g+z same time or z+j same time or g+h same time or h+j same time
and that i get then awawawawaw or asasasasasasas or wdwdwdwdwdwdwd
that when i press 2 buttons same time it switch output ever 1 char
and when i use your answer and holz z it spams wwwwwwwwwwwwwwwwwwwwwwwwwwwww
and if if press then z+g it only continou spaming wwwwwwwwwwwwww not wawawawawawawawaw
thans
Karoline
According to your "answer", you are targeting a keyboard behaviour that's not standard. That is, using remapped keys with AHK or not, when pressing two letter keys simultaneously, solely the key pressed at last will continue firing.
Here's an example on how you can achieve an alternating behaviour instead:
SendMode, Input
g & z::
z & g::
Send, aw
return
g::Send, a
+g::Send, A
z::Send, w
+z::Send, W
You will have to explicitely define every custom combination with an & modifier, which makes them prefix keys. This comes with an upside and with a small downside: The upside is, you can now keep pressing your combination and it will continue firing two keys in an alternating fashion; no loop or the like required. The downside: You need to reconstruct prefix hotkeys' original behaviour. In our example, that's sending a lowercase or uppercase letter.