Until speaking stops

I haven't approached this question before, maybe it is glaringly obvious, but I have a macro that speaks the selected text. That is, it copies the selected text to the clipboard and speaks the clipboard.

I want to create an until action that will allow an action to continue as long as this speaking of the clipboard continues. Easily done?

I am thinking that I have become spoiled using iOS devices for reviewing the text I am reading as in iOS each spoken word is highlighted and the page automatically scrolls.

I am thinking that, if I can get the macro to be aware when it has finished reding the selected text then, I can figure out a good distance and wait interval for the scroll wheel to keep scrolling the page at some reasonable interval so that the text - or the paragraph in general, is continuing to rise to the top of the page. I can play with the parameters better with how long to pause and how far to scroll after I understand how to set this to a condition (until).

Thanks.

Is the purpose of this problem intended to let you write a macro that scrolls text up in some application while it's reading it? If so, then maybe there's a better solution.

Eg, I could probably come up with a macro that sends a single sentence at a time to the Speech action, in which case it would actually know where it was in the reading (to the nearest sentence) meaning that it could scroll with perfect accuracy.

But as a direct answer to your question, immediately after your Speak action, set a global variable such as Finished=1 and then in your calling macro you PAUSE UNTIL FINISHED=1. I must be falling to understand because that's too easy.

Yes, I wanted the page to scroll while reading it out loud. I don't mind that it pauses for a number of seconds, calculated from the characters selected and how long my speaking engine takes to speak x amount of text. I could then pause for x number of seconds, then scroll down 50 clicks, and repeat until all of the text on the clipboard has been read.

The problem I have is that, in real life I can do other tasks when the clipboard is being read such as scroll down the page, I have not found way to have the script do the same thing. It seems that the script cannot pause a task that occurs at the same time as the clipboard is being read without pausing the reading of the clipboard - which I do not want.

What I wanted was for actions to take place at the same time as the clipboard is being read which are not dependent on the clipboard being read - other than to stop when the clipboard has finished reading the text.

That means that in part one I have the clipboard begin reading the text.Then in part two, which should happen during the same interval as part one, I want a timing interval to occur and scrolling down of the window to occur at regular internals until the clipboard reading task has finished.

So far I have not been able to get this macro to multitask in this manor.

I think it's clearer to me now what you want. But if I understand it, your approach to a solution isn't the best approach. That's why I felt I had to ask. I think your approach could be made to work most of the time, but it is based on averages and assumptions that might change (window size, font size, and so on). Whereas I have an approach that will always work (unless your text isn't broken up into paragraphs.)

Here's my very simple approach which provides the same results and allows the application to do the scrolling itself (at least for Pages it does). As a result of leaving the scrolling to the application, it is 100% guaranteed to leave the spoken text visible on the screen (but usually at the bottom rather than the middle or top. That might be fixable too.)

image

Basically this solution is in a permanent loop. I allow the user to end the macro by pressing the Caps Lock key. However since your Speak action can take a long time, you probably need another macro which calls the Cancel All Macros action. I have bound that to my back quote character which is above my Tab character on my keyboard. As a result of having that shortcut I usually just use infinite loops, rather than using Caps Lock to stop. This is a perfect example of why an infinite loop is justifiable, because you really do need the ability to cancel all macros from a hotkey anyway.

Yes, and, I already have this. I put in a stop all macros a long time back. I am seeking a more elegant solution to see if it is possible to have two things happening at the same time or not when each one requires a KM macro. So far it seems that once the clipboard is speaking, there is no way to scroll - pause - scroll - pause, etc.

Perhaps if I created a trigger for a separate macro? Then when the macro is speaking the clipboard, another macro is going to begin to scroll down. The trick would be to have determined the time of the scrolling by calculating the characters on the clipboard first.

I suppose I can always have the stop all macros stop the scrolling, but the original thought remains that if I calculate the number of characters in the clipboard, and the speaking rate and voice is a known - or is a relatively known quantity, then it should be to set a relatively correct scrolling distance, a relatively correct time of pausing, and repeat until finished.

I could come up with a two macro solution for you. But any solution which can fail when it makes false assumptions about window sizes, font size, and suchlike is not "elegant". I would use the word "kludgy" to describe algorithms which don't work reliably. The algorithm I showed above works 100% of the time.

But if you absolutely insist on an algorithm that uses asynchronous processing, that should be possible. Just bear in mind I don't have your screen size, or your font sizes, so you are going to have to make all the adjustments to make it work at your speed.

One of the tricks I would use to make your approach work is the macOS "say" command which can output text to a FILE without speaking a word. Examining the length of this file would tell you exactly how long the Speak action will take. That's half the battle right there. The next question before you can calculate the scroll speed is to know how long the clipboard is (that's easy to get), how much of the text is currently visible on the screen (not easy) and how much text can fit on the screen (not easy). These last two items are the missing piece of the puzzle. Since you have not told me enough information for me to make guesses to those things, you will have to make the numerical adjustments to work for your screen. Basically I think it will be a simple equation with two values: (a) a value representing how much selected text is on the screen at the beginning of the macro, and (b) a value representing how much text can fit on the screen at any one time. I could probably write all the code except for those two constants which you would have to provide. I could use the values that work for my screen but I have a 5K screen with different fonts from you, so my formula will have to be adjusted by you.

Are you sure you want to go through this? And the point to doing this, when you already have a 100% working code above, is just to learn how asynchronous processes can be made to work?

No, thank you for the offer but, it is too much work for me right now. There is a reason I am using this method and that reason is that I have a lot of writing and editing to do right now.

I have now tested creating a separate macro that will scroll up the screen until the clipboard is a specific value. I was hoping that, once I had the read text aloud macro hand off to this second macro, the first macro would start reading the clipboard. But this is not what it does. It appears that in the KM engine there is no way to run two separate macros at the same time.

In the real world, you can do more than one thing at the same time on a mac like, having the macro reading the system clipboard while you scroll the screen and edit the text. But in the KM world, apparently, this is simply not a possibility.

FWIW, you don't need any info about how large my screen is AFAIK. The selection size is rarely if ever going to be the same. I was suggesting that, since the amount of text will vary each time the macro is triggered according to how many paragraphs I selected to have read back, it should be a relatively straightforward calculation. If I normally use the voice Elex read at medium speed (which is about right) then I would calculate a few paragraphs read in that voice at that speed, repeat a few times for an average, and then I would have a better idea of how long it takes to read x number of characters. After this it should not matter how much text I have selected, 3 paragraphs or three pages of text. By counting the characters on the clipboard and knowing how long it seems to take to read through x number of characters, the scrolling speed should appear.

Of course the screen size is unique to the machine I am working on. My 13" MPB is going to finish a line much more quickly than is a 27" horizontal monitor - but is that the same as reading faster? I think not. As I am thinking about it, the speed of the characters being read should be the same regardless of the screen size or the length of the horizontal line as this is down to the voice I use and the speed of that voice which is going to be a constant - if somewhat text dependent but not line length dependent.

This was a curiosity for me to have something to do to improve a process I am using heavily at the moment. I was hoping to understand a way to resolve the problem but I will stop short of asking you to go to work on a longer solution on my behalf. That is kind of you to offer. But, I need to get back to work now and not spend more time on problem solving. Call it a pleasant distraction, a way to try to learn more about using macros in KM.

Oh well. Perhaps I( will revisit this when things have slowed a bit.

Don't feel bad, but I wrote the code and just finished it. I will upload it now.

My algorithm REQUIRES knowing how many words fit in your window. You said I don't need to know your screen size or font size, but for this to work, I absolutely do. Without that knowledge there's no way for me to predict how fast the scrolling should be.

Why do you say this is not a possibility? I've just done it. I'm not sure if you will like the results, but I did it.

The first thing you need is a macro, which I call SpeakA, which contains this action:

image

That's the routine that speaks text asynchronously (hence the A). There's nothing in this macro that makes it run asynchronously, that's flagged in the other macro, which is as follows. The explanation follows the code.

image

Firstly, I'm using the application Pages, so I activate it. If you are using something else, feel free. But you may have to change some of the timing.

Secondly, I assume the user has already selected the text and triggered this macro with a hotkey.

Thirdly, I initiate the asynchronous reading of the text. Note the flag indicating asynchronousness.

Fourthly, I use a shell command to calculate the size of the spoken text file. it assumes the default voice. If you don't prefer to use the default voice, feel free to change it. But that might change the timing.

Then I paused for three seconds. That's a totally arbitrary value. My program doesn't know how far down the selected text begins. If it starts at the top of the screen, you may want to change this to 30 seconds to get the speech synthesizer theorizer a chance to reach the middle of the screen before the rest of the code kicks in.

Lastly, there's a loop that scrolls for the exact duration of the size of the speech file. There are two very important numbers in this code that I told you in a previous post you would have to tinker with. One is 0.07 which is how long each tick of the scroll will pause for. This is basically the speed of the scrolling. It is very dependent on your fonts and screen size (even your video driver). Since I have no way of knowing those values for your screen, you will have to experiment with that. Second is the value 8. This value divides the size of the speech file (which represents the duration) by a constant that tells KM how long to keep scrolling for. This value may also need to be tinkered with at your end, depending on things like the voice you choose and the speed of your CPU. Of course I can't predict those things.

This code works perfectly for me. It does exactly what I think you described. It keeps the part of the selected text that's being read right in the middle of the screen until the end of the speech. It's perfect.

Try my code and tinker with the two values, 8 and 0.07. Maybe those values will work for you. But on my screen my fonts are very large, and yours are probably smaller. So I'm guessing you will have to change those values.