My goal in two previous weather related projects was to include oral
weather reports and forecasts in my home automation system. It is not too
hard to do. Once the text is available, a two-step operation is all that is
required to hear it. First a speech synthesis engine produces a sound file
and, secondly, a utility plays the sound file. I use
wav file and
aplay from ALSA as a player. Both these programs are available
on the Raspberry Pi Rasbian, the Orange Pi Zero
Armbian and on my Ubuntu
17.10 desktop machine.
Previously, I used a straightforward naive approach, which consisted of two consecutive calls to the operating system returning to the program only after both utilities had terminated. This is what it looks like in Python and Free Pascal.
This is not desirable because a forecast can take up to two or three minutes during which time the user interface is frozen. Maybe two or three days of forecasts is enough, with no need to go on and on for another seven or eight days. I wanted the following.
- Focus must return to the calling program as soon as the sound. file begins to play.
- The calling program must be made aware when the utility has finished playing the sound file.
- The calling program must be able to terminate the playing of the
sound file any time after it started to play. This last requirement is
actually a bit more complicated than it sounds. I want
- the ability to stop playing the sound file whenever the user signals that need, and
- the ability to optionally stop playing the sound file or continue to play the sound file when the calling program is itself terminated.
Running a process in a thread seemed like the obvious solution. Trouble is that I didn't know much about threads. In many Pascal/Delphi programs I managed to avoid them by using the application idle loop to do asynchronous tasks. Most times that was just to update the state of a program's GUI. Often I eliminated that need by explicitly updating the display only when necessary.
As usual when tackling a new problem, I tried to get information on the subject. And as usual, it was rather incomprehensible at first and I was having some difficulty in getting this to work as I wanted because of my shaky understanding of the mechanics of threads. Again, I have to thank the community for being so willing to share knowledge and experience. The Thread (computing) article at Wikipedia and the Threads course notes by John T. Bell at the University of Illinois, Chicago were helpful.
Threads are tasks that are executed in "parallel". They appear to be working simultaneously, but on a single core computer that is not possible. Instead, all the threads are being performed a few instructions at a time. With modern multi-core computers, threads can truly execute in parallel if assigned to different cores. All threads could be given an equal slice of core time and be executed one after the other in round-robin fashion. Or certain threads could be given a higher priority meaning they get a larger slice of time. Back before OS X, the Mac OS required threads to cooperate meaning that each had to yield control of the core after a reasonable amount of time used so that the other threads could be given a slice of time also. Just think about it. If you made a mistake in a thread, you could easily bring down the whole system. As far as I know, main stream OS now use preemptive scheduling so that other threads will continue to be executed even if one has gone off on a wild goose chase. The allocation of time, scheduling and the switching from thread to thread is all done by the operating system.
Synchronising threads can be a problem. What if thread A needs results calculated by thread B? A does not know when B will be finished, so it may have to wait for it to be done. And two threads could be trying to use the same resource at the "same time." And a resource could be something as innocent looking as a string or other variable. Will the variable contain what A wanted to write to it or what B wanted to write to it or some mishmash?
The Free Pascal run time library
TThread class is a wrapper
around the OS threads so, thankfully, we don't need to know much about the
bare metal aspect of these little beasts. The basic idea is to create
a descendant class of
TThread overriding a few methods. Usually
the constructor is used to set up the particulars of the descendant
threads. And then the
Execute procedure is overridden because
that is where the thread will be doing its task.
I did run into synchronisation problems. Remember that an application is itself a thread so that when a TThread is created, it has to be careful if it interacts with any user interface object or anything that in turn accesses application resources. But my biggest problem was with memory leaks when interrupting the sound player. I found the detailed help I needed in the Lazarus forum. And I have to thank search engines because I have yet to learn how to use the forum search engine efficiently. I strongly recommend the four pages of entries on the topic Playing with thread, memory leaks.
It started to sink in by the time I got to the answer from Blestan. Since I was setting the thread
freeOnTerminate property to
terminate were to be used. Furthermore,
all the cleanup had to be performed in the
while loop. So here is the small unit I wrote based on
The work is done by a
TPlayThread. Just about everything happens in the overridden
execute method. It starts by creating a process which launches
aplay utility and returns immediately (notice that the
poWaitOnExit is not included here). The following
while loop will run as long as
aplay runs. On
completing the loop the process is freed and the
event is fired. Two flags are used to interrupt the
StopPlay flag which is set by calling the
StopPlaying function. The
AppClosing flag is set
finalization and will only be true when the
application is, surprise, surprise, closing. When
completed, the thread is automatically freed. No matter what, both the
process object and the thread will be freed, so there will not be a memory
while loop is interrupted before
has finished running, the latter will in turn be interrupted if the
StopPlay flag had been set using the
function. That is how it will be possible for the user to interrupt
aplay. Remember the application thread is running in parallel
with our thread that is doing basically nothing. So the application user
interface is not frozen. If the interruption of the
was caused by the termination of the application, then
will be stopped only if the
FTerminateProcess field is true.
Otherwise, it can continue playing its merry tune even as the application
The need to signal the coming end of the application in order to free the
thread was outlined in the forum discussion by Fred Vs and BlueIcaro. However, contrary to Blestan's admonition,
they both invoke the thread
terminate method while the
freeOnTerminate property is set to true. I did not like using
a global boolean variable nor using the GUI event to look after freeing a
thread. It seemed like something I would easily forget to do in a future
use of the unit. As a first solution, I used the
terminated field of the
object as a flag in the
AppClosing. That too was not satisfactory, because
playunit is based on threads and processes which could be useful
in console applications without forms and the
I was happy to discover that a simple flag set in the
part of the unit was just as good.
It is mandatory to test for the application termination (i.e. checking if
AppClosing or or
application.terminated is set to
true) before triggering the
OnPlayDone event. It
that test is not performed, there is every chance that a hard to trace
exception will occur because in all likely hood the event handler will have
been destroyed by then time the event is fired. And note
is called indirectly with the
syncrhonize procedure. That
takes care of any synchronisation problem that could be caused by the
The unit does not expose much. The
TProcess objects that are created are entirely private.
StopPlay flag is set indirectly with the
StopPlaying procedure. The
Play procedure is
used to launch
aplay in a thread. Its arguments are
the name of the file to play, the address of the
handler and a boolean flag to automatically stop or not
the application is shut down. These last two parameters are optional and
aplay will be automatically shut down.
The small test_thread
project can be used to see how
playunit is used.
Compile it and making sure that the build mode is
Debug(Projet/Project Options/Compiler Options/Build modes Debug). Then start a terminal and start the application. Initially, the Stop button is disabled. When the Play button is pressed it is disabled, the Stop is enabled and the
Playprocedure is invoked.
wav file is finished playing because
aplay arrived at the end or because the Stop
button was pressed, the
OnDoneEvent handler is fired and
it restores the buttons enable property to their initial values. Pressing
the Quit button will interrupt
depending on the state of the checkbox.
The program has a different behaviour if run within the Lazarus IDE.
No matter the setting of the checkbox
aplay will be shut down
by the IDE when the Quit button is pressed.
If the test application is terminated in the first few seconds after
aplay, the latter will be terminated along with the
application no matter the setting of the checkbox. Don't know why that is.
Oh well, can't win them all.