Tuesday, October 29, 2024

Chorus pedal

A few weeks ago, I was minding my own business whilst surfing a little on the Internet, when suddenly popped up an advert from Ali Express. I willingly confess that only rarely do I look at these adverts, and I consider them very much a nuisance. This time, however, the ad caught my eye: guitar pedals were being sold for about 3.5 NIS (as I don't recall the exact price, I'll call it about 0.75$). This of course is a ridiculous price and should be considered a loss leader.

By the time I decided to purchase a chorus pedal, its price had shot up to about 14.5 NIS (about 3.80$) - still a ridiculous price. So I ordered the pedal: how can I lose at that price? I wanted to add a phaser, but whilst I had been fiddling with the order, the price of the phaser had tripled to about 43 NIS - still about 40% of how much such a pedal from a no-name supplier would cost. I passed on this opportunity.

One might ask why I want to buy a chorus pedal (even at its ridiculously low price) when I already own the multifunctional guitar effects pedal1 that I bought in February. This pedal has a chorus effect and in fact recently I've been using the chorus preset that I defined several months ago. But over the months I have become less and less satisfied with the multi pedal:

  1. When the pedal is turned on (i.e. connected to power, although the pedal has its own power supply), the bluetooth connection is turned on. This causes a small blue light to flash continually. I don't use bluetooth. After trying to turn it off and failing (but causing something else to happen), I decided months ago to ignore it.
  2. Again, when the pedal is turned on, the first preset is automatically selected and engaged.
  3. Whilst playing guitar and operating the pedal, I occasionally hit the wrong foot switch.
  4. The idea of presets is very good, but the way that it is implemented is poor - to my mind. Just getting into preset mode is difficult. Two foot switches have to be pressed simultaneously, but I can never remember which two, and anyway I have difficulty pressing two at the same time. 
  5. Should I manage to enter preset mode, I have no idea of what the current values for the different parameters are. Should I wish to reduce the volume for preset 3 (the chorus), I have to redefine all the parameters and so probably end up with something else from what I wanted. It would be good if there were little displays next to each parameter - or that the knobs are automatically turned to match the saved values - but I understand that such improvements would probably cost no small amount of money, thus jacking up the price of the unit and making it less attractive than individual pedals.

So for the time being, I'm going to remove the multi-pedal and replace it with the minuscule chorus pedal. I assume that the quality of the pedal will be reasonable.

At the same time, I have been bombarded with letters from Ali Express (well, one every two days) encouraging me to buy from them again. Temu doesn't do this.

Internal links
[1] 1721



This day in history:

Blog #Date TitleTags
10829/10/2007New computerComputer
51729/10/2012Remixing songs and playing liveMIDI, Guitars, Antibes
77029/10/2014Importing purchase ordersProgramming, ERP
89929/10/2015Vinyl log 24 - 29 OctoberVan der Graaf Generator, 1975, Vinyl log
154229/10/2022The 8-puzzleProgramming, Puzzles
168229/10/2023Farewell, Avon SG copyGuitars

Saturday, October 26, 2024

Israel on a Friday night at the end of October 2024

I went to bed slightly early yesterday evening, partially because I had been late to bed the night before (band practice) but primarily because the evening news programme had been very depressing. 13 soldiers were killed in the previous 24 hours, two - one of whom was old enough to be the father of the second - from the same kibbutz (not mine). Earlier in the day I had received a message saying that the brother in law of a senior employee in my company had been killed in Lebanon, and I think that it was this 51 year old veteran who is past the age for serving but still volunteered.

Cut to the two idiots in the government who want to renew Israeli settlements in Gaza, 18 years after separation. If the world doesn't like us now, it certainly won't if settlers return. I suspect that a large proportion of Israelis are against such settlements, and statements such as these only cause the dislike/hatred/disgust to grow against these two. Also mentioned were the government intentions to pass laws regarding evasion of military service for the ultra-orthodox and renewal of intentions to 'reform' the judiciary system.

Who am I to talk? Two colleagues of mine live in kibbutzim in the north of Israel and were evacuated when the war started over a year ago. Another friend also lives in a kibbutz in the north of Israel, but further from the border than the 'evacuation line', so she has stayed at home. Last weekend, missiles landed near her kibbutz and started three fires that took some time to get under control.

I'm Teflon. This war has barely impacted my life in the same way that COVID had little effect - I often joke (although I'm serious) that I actually benefited from the pandemic in that it gave me the legitimacy to work from home. But that is not to say that the war has had no effect on me: I noticed a few weeks ago a dropping of interest of mine in everything and I suspect that this is due to the long drawn out war of attrition.

I had problems getting to sleep last night: first the television news, then the hot wind that has been blowing for the past few days has dried out my skin and the dust blown about is irritating my nasal passages. Just before midnight I took one of the relaxation pills that I bought in Milano, and the next thing I knew, it was 7:30 am. I had a dream about volunteering for reserve duty - I would do the same as I did in the (first) Lebanese war in 1982 when I packed kits for medical personnel. In the dream I was turned away.

I discover that Israel attacked Iran early this morning. I was out of the world but my wife said that she heard wave after wave of aircraft passing during the night.

Over the past few days I have been reading in small doses Yuval No'ach Harari's latest book, "Nexus". I quote: Nexus looks through the long lens of human history to consider how the flow of information has shaped us, and our world. Taking us from the Stone Age, through the canonization of the Bible, early modern witch-hunts, Stalinism, Nazism, and the resurgence of populism today, Yuval Noah Harari asks us to consider the complex relationship between information and truth, bureaucracy and mythology, wisdom and power. He explores how different societies and political systems throughout history have wielded information to achieve their goals, for good and ill. And he addresses the urgent choices we face as non-human intelligence threatens our very existence.  

I originally thought that the book would be about the flow of information in computers, but it starts by examining how the flow of information affects democracy and totalitarianism, and how inventions of the past 150 years (telegraphy, telephone, wireless, computers) have allowed these to flourish. Reading about populism is very frustrating, especially as a populist candidate again is running to become President of the USA. 

Harari also gets in a few mentions of Israel, and reading between the lines, one can see his opposition to the populist policies of Netanyahu et al.: one of the first things that a populist regime attacks is an independent legal system. After all, one of the tenets of populism is that it "speaks the will of the people", and if one is not a supporter of the populist policies then one is an enemy of the state.

When are there going to be elections here?

Internal links
[1] 1589


This day in history:

Blog #Date TitleTags
21326/10/2009Entity relation diagrams (ERD)Firebird
42026/10/2011John McCarthyObituary, RIP
89726/10/2015Goodbye MochaDog
89826/10/2015Remembering MochaDog
118026/10/2018First rainWeather
135326/10/2020 I don't take any pleasure in being a CassandraPersonal
143226/10/2021My blood pressure is balanced!Health, CPAP, Blood pressure

Thursday, October 24, 2024

Midnight and Blue

A new Ian Rankin novel about John Rebus, retired DI, set in prison. I haven't read much Rebus over the past few years for various reasons, so some of the continuity of the last few novels - and especially why Rebus was in prison - passed me by.

This is a typical Rebus novel, and I don't mean that disparagingly. It's of high quality, but the formula is familiar, with several seemingly different strands becoming one. Several characters from previous novels continue to appear; I suppose that one day I'll have to make myself a Rebus database and track all the characters.

One person's characterisation has changed somewhat: DI Malcolm Fox, originally from The Complaints. Here he seems to be enthusiastic but incompetent.

I don't what to give away very much but I do have to comment on what is basically the primary clue. At the beginning, someone plays a piece of music -

He had switched on some music, which they listened to in silence for the first five or so minutes.
‘So who is this?’ Esson eventually enquired.
‘Pentangle.’
‘Is it recent?’
He took his eyes off the road briefly. ‘Late sixties. Seriously, you’ve never heard of them?’ She shook her head. ‘How about the Fairports then – Sandy Denny and Richard Thompson?’
‘Sorry.’
‘Incredible String Band? They were from Edinburgh.’

Later on we read a fair amount about 'The Great Valerio', from the classic "I want to see the bright lights tonight" album and receive an explanation of the lyrics. Ian Rankin used to weave the titles of Rolling Stones' songs in his books (there are books entitled "Beggars banquet", "Blacl and blue" and "Let it bleed" as well as other references) but now he seems to be channeling the late Peter Robinson and including Richard Thompson songs.

Internal links
[1] 280



This day in history:

Blog #Date TitleTags
21124/10/2009Wizz Jones: The legendary me - and musings on music samplersNice enough to eat, Fairport Convention, 1971, Dave Evans, The village thing, Wizz Jones
29724/10/2010Copper socksHealth, Copper
76824/10/2014User resistanceERP, DBA
89524/10/2015Living in the past1970

Wednesday, October 23, 2024

Blog manager startup code

I think (and hope) that I've found the magic combination to allow the database connection on my development computer to contain the name of the database on that computer, but allow that to be changed without error on my laptop (see here for previous discussion). The dpr code and the initial code from the datamodule are as follows

var db: integer; begin db:= TAboutBox.Execute; Application.Initialize; Application.CreateForm(Tdm, dm); dm.OpenDatabase (db); Application.CreateForm(TMainForm, MainForm); Application.CreateForm(TDimmerForm, DimmerForm); Application.Run; end. ... procedure TDm.DataModuleCreate(Sender: TObject); begin // prevent the compile time definition causing a problem with sqlconnection1 do begin close; params.values['database']:= ''; end; end; procedure TDm.OpenDatabase (index: word); var pname, dir: string; begin case index of 0: begin pname:= 'Perceptions'; fileprefix:= pname; end; 1: begin pname:= 'PPP'; fileprefix:= 'Programming pitfalls in Priority'; end; end; with TRegIniFile.create (regpath) do begin dir:= ReadString ('firebird', pname, ''); free end; with sqlconnection1 do begin close; params.values['database']:= dir; loginprompt:= false; end;

Initially the database 'number' is obtained from the 'about' dialog. The datamodule starts up with the database value defined, but the ModuleCreate code clears it. Then the OpenDatabase procedure is called with the index; this sets some local variables then obtains from the registry the name of the database required.

So far, so good.


I was thinking about the function that returns a new instance for the temp table. This was as simple as could be

Function TDm.NewInstance: word; begin qNewInstance.Open; // select max (instance) from temp result:= qNewInstance.fields[0].asinteger + 1; qNewInstance.close; end;

It occurred to me, however, that there could be a call to NewInstance before any values with the new instance had managed to get written to the table, in which case the second call to NewInstance would be given the same instance number. I had to 'protect' this value by immediately entering a tuple with this instance number by means of a new insert query, qSentinel. Not satisfied with this, I thought it best if the entire function was protected by a mutex - I borrowed the code from the linked web site. 

Quite pleased with myself, I went looking for examples where the database has to be changed at run time. I haven't found anything suitable yet, but I did find something very interesting: the sqlconnection has a method called 'ExecuteDirect' that receives a string and executes it. As it happens, I was thinking of something like this yesterday afternoon, as that's how Priority works, but I didn't think it possible in Delphi. Well, it is, and here is the final code for the NewInstance function. I'm sure that I'll find many places where I can use 'ExecuteDirect' instead of a predefined query (note that this requires no parameters - I have to 'inject' a value for 'instance').

Function TDm.NewInstance: word; var s: string; begin mutex.Lock; qNewInstance.Open; result:= qNewInstance.fields[0].asinteger + 1; qNewInstance.close; s:= 'insert into temp (instance, id) values (' + inttostr (result) + ', -1)'; sqlconnection1.executedirect (s); mutex.Unlock; end;

I feel at times like Lawrence Waterhouse in 'Cryptonomicon' in chapter one, 'Barrens', where he is out with the fictional Alan Turing and Rudy von 'something or other', when Turing says "Shut up about Leibniz for a moment, Rudy, because look here: You—Rudy—and I are on a train, as it were, sitting in the dining car, having a nice conversation, and that train is being pulled along at a terrific clip by certain locomotives named The Bertrand Russell and Riemann and Euler and others. And our friend Lawrence is running alongside the train, trying to keep up with us—it’s not that we’re smarter than he is, necessarily, but that he’s a farmer who didn’t get a ticket." In other words, I learned the basics of Delphi from a few books and from magazine articles, but after that, I was on my own and so probably there are many topics that I don't know about that could ease my work.

Internal links
[1] 1844



This day in history:

Blog #
Date
Title Tags
1352 23/10/2020
Overcoming the 'leading zero' problem in Excel Programming, Delphi, Excel, Office automation

Tuesday, October 22, 2024

Jasmine Myra live in Portugal

I had set YouTube to play videos connected to the string 'Gondwana' (the label founded by Matthew Halsall). Some of the music was mildly interesting and some not to my taste, but my attention increased when I noticed that a video of Jasmine Myra playing in Matosinhos, Portugal, had started.

This was filmed at (presumably) a jazz festival held on 14 July 2024 and appears to have been filmed by a mobile phone, as the picture is not always steady, there are no cuts to another camera angle, and the sound includes various ambient noises such as gulls, dogs and children. 

As the date suggests, the set list is comprised solely of tracks from JM's second album, "Rising", and the musicians appearing here are mainly those that appeared on the album, namely Alice Roberts (harp), Jasper Green (keys), Joe Wilks (?) (bass, replacing Sam Quintana), Ben Haskins (guitar), Greg Burns (drums, replacing the regular drummer George Hall) and the elusive Arran Kent (bass clarinet, flute). I write 'elusive' as although Arran played on both JM albums, he didn't appear in any of the previous videos that I have seen

It seems that every time that I see JM, her appearance changes. Here her hair looked at first as if she was wearing a bobble hat, and she has lost her glasses.

The music is very placid and low key; even 'Knowingness' doesn't catch fire, and as such is slightly disappointing. Maybe it's because of the replacement drummer and bassist.

I wrote previously about the economics and logistics of touring with a seven/eight piece band with a harp. It occurred to me yesterday that the harp could be hired in every city where the band plays, thus avoiding the logistical problem and saving wear and tear on the harp. After all, pianists don't tour with their pianos; venues provide a piano.

Internal links
[1] 1776
[2] 1731



This day in history:

Blog #Date TitleTags
10622/10/2007Captain Coulston (even more Folktronix)MIDI, Steeleye Span, Fairport Convention, Reason, Folktronix
21022/10/2009DollhouseTV series, Olivia Williams, Dollhouse
64322/10/2013Some DBA thoughtsERP, DBA
89222/10/2015Vertigo and ENGHealth
89322/10/2015Italian ice cream in IsraelPersonal
108522/10/2017A conditional choose-field trigger (for use in a procedure)Priority tips
135122/10/2020New bass guitarMusical instruments, Guitars

Monday, October 21, 2024

More blog manager tricks

I added a graphing unit to the blog manager program; this is almost the same as the unit in the Management program, but with a few small adaptations. The first is concerned with how the data is passed to the unit; in the Management program, a query is passed, whereas in the Blog manager, the data is stored in a new instance of the temp table so the instance pointer is passed to the graphing unit that also disposes of the instance when the form is closed.

In this program, I want the graph to be sorted by ascending values (at the moment, either number of blogs per tag in a given period, or number of blogs per month) - this is something that I learned today. This is achieved by the following three lines - note that the first two access YValues whereas the third accesses XValues.

series1.YValues.Order:= LoAscending; series1.YValues.sort; series1.XValues.fillsequence;

There's another problem with the Blog manager: at the moment, two separate databases can be accessed by the program. This means that ideally the string that holds the location of the database to be accessed should be empty when the program starts. But for development purposes, I need that the location string not be empty; not only that, the location on my development computer is not the same as on my laptop. When the program starts, it automatically accesses the database at the location stored within the program, but this location does not exist and so the program crashes.

I've been dealing with this problem since I added the second database. I had a solution, but unfortunately it got lost presumably when restoring the program from backup yesterday (I had problems with the dimmer form). After thinking about it for a while, I implemented a different solution from whatever solution I might have had previously (I think that this was dependent on the order of form creation in the project's dpr form). I am documenting here in case it gets lost again, or in case I need it for another program.

procedure TDm.DataModuleCreate(Sender: TObject); begin // prevent the compile time definition causing a problem with sqlconnection1 do begin close; params.values['database']:= ''; end; end;

Like every other problem, it looks so simple when looking at it in retrospect. 

[Edit from a day later: unfortunately, the code presented above for solving the db problem does not work. I've tried putting the 'about' form (by which the database is chosen) at the beginning of the dpr unit, prior to any form being created, then having the datamodule created then the main form. This still does not work, so at the moment I have cleared the value of the database string. I wonder whether I can pass the chosen database name during the creation of the datamodule.]



This day in history:

Blog #
Date
TitleTags
5821/10/2006
Busy morningProgramming, Randy Newman, Paul Simon
64221/10/2013
Paul KleePersonal, Bristol Grammar School
108421/10/2017
Sumptuous Saturday Seven - baked hake with potatoes and vegetablesCooking
168121/10/2023
Seventh chordsMusic theory

Sunday, October 20, 2024

Good news for Smiley watchers

To my infinite surprise and definite delight, I have discovered that the son of John le Carré, novelist  Nick Harkaway, has written a Smiley book!

[It is called] Karla’s Choice, a novel revolving around the celebrated mole hunter George Smiley and his enemy, the Soviet spymaster of the title. Set in 1963, it occupies the gap in the fictional chronology between The Spy Who Came in from the Cold and the trilogy of Tinker Tailor Soldier Spy, The Honourable Schoolboy and Smiley’s People, all published in the 1970s. As well as Smiley and Karla themselves, a host of figures from le Carré’s novels and their TV and film adaptations are there: Peter Guillam, Bill Haydon, Toby Esterhase, Connie Sachs and Control, the Circus chief whom Harkaway thinks of as “a monster – a spider in the web”.

Read about it here. There is an extract of the book available at the moment here.



This day in history:

Blog #Date TitleTags
20920/10/2009Lucky breakTV series, Films, Olivia Williams, William and Mary, Cold feet
41920/10/2011Firebird DB management tool (4) - CorrectionsProgramming, SQL, dbExpress
64120/10/2013More health issuesHealth, CPAP
76720/10/2014The seach for serendipityDBA, SQL
89420/10/2015Vinyl log 23 - 20 OctoberVinyl log, 1971
126820/10/2019This must be the place (2)Personal
135020/10/2020DBA updateDBA
168020/10/2023Sorting on two or more columnsProgramming, Delphi, ClientDataSet

Saturday, October 19, 2024

This year's headphones

I came back from walking the dog in the late afternoon, removed the headphones through which I hear music, and ... SNAP! The rigid curved plastic that keeps the headphones' shape snapped. These are the headphones that I bought1 17 months ago, and although they had a tendency to return the songs to the beginning of the playlist at random times, I was very used to these headphones.

Fortunately a few months ago, I bought a similar pair of headphones from Temu as a spare, knowing that one day I would need them - well, that day has come. Of course, the button layout is different, and on these headphones, a long press increases the volume, whereas a short press moves to the next song. The previous set of headphones had the opposite behaviour, where a long press would move to the next song. These phones seem to play only in mono, very strange for these times.

Obviously it will take me some time to get used to the new layout, especially as the required buttons will now be over the right ear, as opposed to the previous set where they were over the left ear. Maybe next time I'll order two sets of headphones so that I won't have to endure culture shock when the first set breaks.

Temu informs me that these headphones are no longer available, and this gives me an insight into how Temu works. Very often, next to an item will appear something like 'almost sold out'; I used to think that this is a marketing ploy to encourage the browser to order today! because tomorrow the item may not be in stock. This actually seems to be true. I get the impression that Temu buys up old stock cheaply from suppliers because once all the items are sold, there are no more. Several items that I have purchased over the past year are no longer available.

Internal links
[1] 1618



This day in history:

Blog #
Date
TitleTags
76619/10/2014
Literature review: first draft completedDBA
126719/10/2019
Juliet (naked) - the filmFilms, Nick Hornby
167919/10/2023
Think again: the power of knowing what you don't knowHealth, Non-fiction books

Friday, October 18, 2024

Completing the auxiliary program

I found another example of a unit that requires heavy processing, so I decided to move the calculations to the auxiliary program. As opposed to the first procedure in the auxiliary program that calculates correlations, when this procedure finishes, it 'tells' the main program to locate a specific form and update its display; if the form is not displayed then it should be displayed. This part was interesting to program and displays the utility and capabilities of the auxiliary program.

Unfortunately, translating the database code to the format required had me knocking my head against a wall for two hours. I wrote previously that I am using the InterBase components as this makes initialising the database easier especially as I am writing a text-only unit. Whilst it may make initialising the database easier, it makes it harder to write the rest of the code.

I considered this problem whilst walking the dog and decided that I was going to convert the program to dbExpress. The first option would be to see how a visual program defines the database connector then use that in the auxiliary program. If that didn't work, then I would convert the console program to a visual program that won't display its forms. Fortunately the first option worked.

I opened a datamodule from one of my programs and converted the dfm file to text. Here are the definitions that I needed

object SQLConnection1: TSQLConnection ConnectionName = 'IBConnection' DriverName = 'Interbase' GetDriverFunc = 'getSQLDriverINTERBASE' LibraryName = 'dbexpint.dll' LoginPrompt = False Params.Strings = ( 'DriverName=Interbase' 'Database=.... 'RoleName=RoleName' 'User_Name=sysdba' 'Password=masterkey' 'ServerCharSet=' 'SQLDialect=1' 'ErrorResourceFile=' 'LocaleCode=0000' 'BlobSize=-1' 'CommitRetain=False' 'WaitOnLocks=True' 'Interbase TransIsolation=ReadCommited' 'Trim Char=False') VendorLib = 'gds32.dll' Connected = True Left = 56 Top = 8 end

This became converted to

connection:= TSQLConnection.create (nil); with connection do begin with params do begin values['database']:= s; values['DriverName']:= 'Interbase'; values['User_Name']:= 'sysdba'; values['Password']:= 'masterkey'; values['SQLDialect']:= '1'; values['RoleName']:= 'RoleName'; values['CommitRetain']:= 'false'; values['WaitOnLocks']:= 'True'; values['Trim Char']:= 'False'; end; VendorLib:= 'gds32.dll'; LoginPrompt:= false; LibraryName:= 'dbexpint.dll'; ConnectionName:= 'IBConnection'; DriverName:= 'Interbase'; GetDriverFunc:= 'getSQLDriverINTERBASE'; connected:= true; end;

Once I got past this, I quickly converted all the queries from TIBQuery to TSQLQuery and deleted all the lines that were concerned with transactions. For a change, this worked almost straightaway (there were a few minor matters that required manual changes).

This procedure defined a new paradigm for forms and I'll probably find more units that can be converted to this approach. I don't intend to write anymore about this auxiliary program.



This day in history:

Blog #Date TitleTags
29618/10/2010Mocha too goes on holidayDog
41818/10/2011Dennis Ritchie: The man who created UnixObituary, RIP
117918/10/2018Conditional choose-field trigger (2)Priority tips

Thursday, October 17, 2024

Extending the auxiliary program

Pretty keen - yes, my hobby keeps me busy
and if I talk to myself, what's the crime?

- Peter Hammill, "Last frame"

After a good night's sleep, my right brain came up with an interesting idea that extends yesterday's solution 1 of an external program to calculate correlations. What would happen if I had another calculation that required heavy processing? Would I create a new external program with its dedicated messages?

No: all I need to do is create a general, auxiliary, program to which is passed a string of parameters, where the first parameter is the function number within the auxiliary program to be executed. When that function completes, it sends a message back to the main program, where one parameter is the function number and the second parameter is the data instance number. This reminds me vaguely of the old DOS Int 21h interface.

As this auxiliary program is going to support a variety of calculations, it makes sense to have certain portions global to the program - the initialisation of the database along with the NewInstance and CloseInstance function/procedure. Then all the specific correlation code can go into one procedure with its local variables.

As I mentioned yesterday, I am using the InterBase components; this makes initialising the database easier especially as I am writing a text-only unit. One major difference between the InterBase components and the dbExpress set is that the first set uses transactions. I discovered in a painful manner yesterday that in order for a query to insert or update data in the database, the transaction has to be committed, otherwise nothing happens. Not only that, the transaction has to be set as non-active after the commit so that it can be reused. This flag setting seems to be required after every database action, regardless of whether its an insertion or a selection. This has tripped me up several times, and I'm not sure if I am yet capable of writing a sequence of SQL commands without getting an error message about the transaction.

As this blog's epigraph puts, "yes, my hobby keeps me busy". I enjoy programming - or at least, defining what has to be programmed. As code rarely works the way it is intended first time, I have to debug a certain amount/great deal, and this can be tedious.

Internal links
[1] 1839



This day in history:

Blog #Date TitleTags
29417/10/2010Sweet and sour chickenCooking, Slow cooker
29517/10/2010Project Management courseMBA, Project management
41717/10/2011Getting ready for the KindleKindle, E-book
98317/10/2016Who's watching who?Grandfather
134917/10/2020Completing the story of porting an application to Windows 10/Delphi 10.2Delphi, Unicode

Wednesday, October 16, 2024

Removing the thread code and finding a different solution

After several hours of testing, I came to the reluctant conclusion that nothing works in the program when this type of thread is executing - somewhat negating the whole point of having a thread! I've restored the program to how it was on Saturday night: the interface form calculates the correlations and saves the data in an instance of the temp table. A secondary form is called to display the data.

I've been experimenting on a separate copy of the program units, and it appears that something connected with the database code in the thread is causing the problem. I'm using completely different database components and not accessing any units whatsoever but there the problem still exists.

So, I wondered, what else can I do? An old solution popped into my head: execute a separate program to do the work. This program can be passed whatever parameters are necessary, and can send a message to the main program that data is waiting to be displayed. But it's not always necessary to execute a separate program: if there aren't that many data points to be calculated then it's quicker to do this within the existing program. I determined that - on my computer - 2500 records is approximately the tipping point.

The first part of the code - entering into the temp table the records that will act as the input for the correlation code - remains in the interface unit. Instead of having to issue a separate query in order to determine how many tuples have been inserted, I simply store the return value of the ExecSQL function. Almost always I ignore the return value, which is the number of tuples affected by the query. In this case, the number of tuples will be the number of tuples inserted which is the total number of examinees. 

If this number is less than 2500 then the original code executes within the interface unit; at the end, the son form is created directly to display the data. Should there be more than 2500 tuples, then first a string is built with all the parameters that need to be passed, eg '21 15 12 01/01/22'. Then the external program is executed, receiving this string. It turns out that almost all of the thread code that I wrote could be transferred without change into the external program, including all the database code. In order to simplify matters for myself, I am using the InterBase components as opposed to the dbExpress components that I normally use. Again, this program does not include any module from the main program.

The final line in this program is 'sendmessage (hwnd_broadcast, RegisterWindowMessage ('Show correl'), rinstance, 0)'; this sends a message to every open program on the computer (because the program doesn't know who executed it) with a message number that is not necessarily constant; also using the RegisterWindowMessage technique means that the message number is known only to this pair of programs. I discussed1 this Windows technique way way back in 2009. The call arrives at the overridden DefaultMessage handler that calls the secondary form to display the data.

Finally, success.

Internal links
[1] 176



This day in history:

Blog #Date TitleTags
134816/10/2020PrioXRef 3 - insert without parametersProgramming
153816/10/2022Indexing blogsProgramming, Meta-blogging, Blog manager program
167816/10/2023The running graveCormoran Strike

Monday, October 14, 2024

Terminating threads

Yesterday1 I wrote about threads, noting that the only problem that I've found seems to be that if the window with the user interface (which calls the thread) is closed before the thread finishes its work, the thread appears to die. I searched the web for a description of this problem and how to solve it. 

This tutorial holds the answer. Although it's written describing the case of a main program and a thread, it appears to be suitable for a 'son' form that is calling a thread. The first thing to do is to maintain a count of threads invoked by the form

Constructor TCorrel2Scales.Create; begin . . . threadcount:= 0; end;

Whenever a new thread is created ('DoCorrelThread (id1, id2, fdt ...'), the variable 'threadcount' is incremented. Two procedures have to be added: one is private ('HandleTerminate') and one is an event handler ('FormCloseQuery') - as follows

procedure TCorrel2Scales.HandleTerminate(Sender: TObject); begin Dec (threadcount); end; procedure TCorrel2Scales.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin if threadcount = 0 then canclose:= true else canclose:= false; end;

No change was necessary in the thread unit. I tested this by first running a correlation of two scales from 2018 - many data points - before I added the above code (actually I had added it but neglected to connect the 'FormCloseQuery' handler to the 'OnFormCloseQuery' event) then closing the form (TCorrel2Scales) before the thread terminated. This caused the program to crash. After I connected the event to its handler, the same test worked - the form would not close until the thread had terminated.

So now I know.

Addition from a few hours later: it seems that I missed something in the tutorial. The thread has its OnTerminate event point to HandleTerminate, but in this example the thread is created within the calling form and so this works. In my case, the thread is created in a separate unit and so it would seem that there is no value that can be passed to the thread's OnTerminate event. 

This is solved by defining a new type, TMyProc = Procedure (sender: TObject) of object. Then I can pass the HandleTerminate 'variable' to the thread code. In non-Delphi environments, this is known as a callback and occurs quite frequently in Windows code; even I've used it albeit very rarely.

While all of the above prevents closing the interface form while there is still a thread in existence, it doesn't solve - in fact, dodges - the original problem: how can I close the interface form while there is still a thread in existence. I suspect that the solution is to have the main form create the thread. I tried this but it appeared not to work: I still could not close the interface form. I shall have to work on this some more.

Internal links
[1] 1837



This day in history:

Blog #Date TitleTags
14014/10/2008Where have I been all these months?Literature, Peter Robinson, David Lodge
41514/10/2011Dieting has a number of destructive side effectsMartin Seligman, Diet, Acupuncture
51614/10/2012Friday is cooking dayCooking
76514/10/2014Some days you're the pigeon and some days you're the statueDBA, Films
108314/10/2017The seven stage model for developing enhancementsDBA
126614/10/2019Priority: LIKE cannot accept a variablePriority tips
143014/10/2021Potassium levels in foodHealth, Food science, CPAP, Blood pressure
153614/10/2022Gillian McPherson1971

Sunday, October 13, 2024

Correlations and threads

I have a non-specific memory of sitting at my desk when I was a student in London, calculating correlations that I needed for graphing data. Correlations measure the connection between two non-connected variables; if one increases as does the other by the same percentage, then there is a perfect correlation between the two. The two variables can have no correlation whatsoever or they also could have a negative correlation (one goes up while the other goes down). I don't recall how I learned about correlations - presumably we had a course in statistics during the degree studies, but I have no memory of this whatsoever. Correlation is another arrow in the statistics quiver*.

On Friday, in the context of our flagship database that stores data originating in psychological questionnaires, the OP wanted to know the correlation factors between two scales. This is done by first finding all the clients from a given date who have values for both scales, then calculating the mean and standard deviation for each scale. Then one sums (the difference between the observed value for one scale and its mean) multiplied by (the difference between the observed value for the other scale and its mean); this value is divided first by the product of the two standard deviations and then by number of data points (i.e. clients) less one. A good explanation of this can be found here. I wasn't fazed by her request as I knew about correlation.

Enough mathematics. Two years ago, I added a correlation function in the program for clients who had answered the questionnaires twice, in order to see if there was any change in their attitude to life (no change would mean perfect (1.0) correlations between the two values). I gave a brief look at what I had written there and shuddered as the code seemed over-complicated. These days I have temporary files1, so the task was somewhat simplified. I wanted to write a query that would return the sum of the multiplied differences, but I messed this up and instead used a cursor that would iterate over the population (i.e. clients) and calculate the total line by line. This worked.

In the afternoon, I took the dog for her afternoon walk. As usual, my mind considered2 the code that I had written in the morning and I realised where my mistake had been in the query (brackets in the wrong place). I then realised that I could use the idea that originated in the blog manager and then transferred to the documentation program of showing the results in a separate window. This way, one can run several correlations and see the differences between them (the original version showed the values calculated by the current parameters; using different parameters would cause the earlier values to be 'lost'). Good idea.

As the Yom Kippur fast was approaching, I didn't have any time to implement these changes, but I wrote the ideas down for later. During the fast, with nothing very much to do (I don't listen to music, work on the computer or watch the television; I either read or rest), I considered the ideas for future implementation. I noted to myself that the correlation function requires quite heavy calculating that grows according to the number of data points (a perfect correlation!) and during this time, the program is not responsive. Could I create a separate thread and have the calculation performed there? This would free up the user interface, making it more responsive. Assuming that this is possible, how would the thread tell the main program that it had finished and that the results could be displayed? Could the thread send a message3 to the main program that would cause a window to open with the newly calculated data?

I have written thread code before with Word4 and with Excel5, but in these cases, the database 'work' was done in the main program and the data extracted was passed to the thread. There is no display problem as either Word or Excel open with the data. I don't know enough about threads to know in advance whether my idea would work.

When the fast had finished and I had drunk and eaten sufficiently, I got to work. First I corrected the correlation code then I added the use of another temporary file and the separate windows showing results. This worked perfectly. Then I created a new unit that had the interface code of the previous unit, but passed the parameters from this unit onto a thread. All the computation code went into the thread; this also means that queries have to be created manually and their SQL code added in the text. I was initially dubious as to whether the queries would need an SQL connection defined within the thread, but using the connection defined in the data module worked fine.

Once I got all the syntax problems sorted, I added a final line to this unit:

sendmessage (mainhandle, WM_ShowCorrel, rinstance, 0);

This is meant to cause the main program to open up a specific window and pass to it the data contained in the temporary table with instance 'rinstance'. To my surprise, this almost worked correctly the first time; in the main program I was checking the value of 'wparam' instead of 'lparam'. Once this was fixed, all the pieces worked together perfectly.

The only problem that I've found seems to be that if the window with the user interface (which calls the thread) is closed before the thread finishes its work, the thread appears to die. I don't know enough about threads to know whether this is standard behaviour or some kind of bug; the thread doesn't have to 'know' who created it, only how it was created (i.e. the parameters). I'll have to find some sources to read about this.

Now I can look for other places that have heavy calculations (mainly those calculating means and standard deviations for a few thousand values) and see how I can use the thread and message method to ease the user interface.

* When I was thinking about writing this blog, the word 'quiver' had disappeared from my brain. It's as if the left hemisphere of my brain created a thread to find the word, as about fifteen minutes later the word 'quiver' popped into my mind when I was thinking about something else. Very appropriate.

Internal links
[1] 1548
[2] 1829
[3] 1310
[4] 1436
[5] 1443



This day in history:

Blog #Date TitleTags
13913/10/2008Fotheringay 2Sandy Denny, Fotheringay
41413/10/2011KindleKindle
64013/10/2013TasksProgramming, Delphi
126513/10/2019Acting like an MBA (a 'suit')Personal, MBA
167713/10/2023Time outPersonal, Song writing

Tuesday, October 08, 2024

Is this a sign that I'm getting old?

In a dream that I had a few days ago, I was trying to remember the word (in English) that is used sometimes in financial terms, despite the fact that its normal meaning is 'non-religious'. After spending some time in bed trying to remember, I eventually got up and went to the computer, at 2 am. The word that I was looking for was SECULAR.

When I awoke, I checked my memory - and the word was gone. I went back to the computer, checked my browsing history and found the word again. This time I wrote it down on a pad by the computer. 

Over the next few days I would check my memory and most times I could not think of the word. Eventually I used a mnemonic - Dracula, although 'regular' would be better - that was supposed to remind me of this word. I could remember the mnemonic but about half of the time I couldn't make the connection between Dracula and secular. It's as if the word has completely disappeared from my brain, or as if my brain is not prepared to store it.

And it's not as if I've never used the word: I'm sure that in my youth I used it frequently (when describing my youth movement) and a quick check shows that four blog entries over the years also contain this word (the last time was just over a year ago).

This is somewhat frightening as I am a person whose livelihood is dependent by no small amount on a good memory. This short-term and very specific amnesia may have been caused by the viral infection that has been plaguing me for the past few days - at least I'm hoping so.

I shall try and find sentences containing 'secular' (like "This person was born secular but turned into an observant Jew several years ago") in order to constantly remind me of this word. A song lyric would be best but I can't remember any song containing this word - even the erudite Peter Hammill has yet to use 'secular'.

This reminds me (a poor choice of verb) of a story by Robert Silverberg, 'How it was when the past went away': terrorists placed amnesifacients in San Francisco's water supply, causing people to forget. But the drug affected people differently: one person forgot who he was, one person remembered everything except for his wife, one person lost the previous nine months, and one - the example that I want to use - is a professional memory artist, the sort of person who can recall the seventh page in the eighth chapter of some book. Some of his memories are intact, but others ... are gone. In the end, he commits suicide (for some reason, I can't locate an e-copy of this story; I certainly have it in print).

That's how I felt: almost all of memory was intact but a certain dictionary entry had simply disappeared and I couldn't restore it. 



This day in history:

Blog #
Date
TitleTags
1708/10/2005
Bar mitzvaBar mitzva
20508/10/2009
The girl who played with fireLiterature, Steig Larsson
29208/10/2010
MisinformationERP
89008/10/2015
How the mind worksPsychology, David Lodge
142908/10/2021
DBA: Pilot study 2DBA
167408/10/2023
The blackest day for 50 years - notes from a peripheral participantIsrael

Monday, October 07, 2024

A year after the massacre

After nine months of needless internal conflict that only encouraged them, our despised enemies took to the stage a year ago and changed our world for ever. We will not forget. We will not forget those who were slaughtered, we will not forget those who were captured and tortured, and we will not forget those for whose return we pray daily.


 


This day in history:

Blog #
Date
TitleTags
5607/10/2006
Strange dream leads to improved algorithmProgramming, The brain
20407/10/2009
Pressure cookerCooking
134307/10/2020
Completing the second version of PrioXRefProgramming

Saturday, October 05, 2024

The swimming season finished rather abruptly

Formally the swimming season was to have ended last Saturday, 28 September, but the powers that be decided that there would be three extra sessions: on the morning of the New Year (Wednesday), Friday (i.e. yesterday) and Saturday (i.e. today). 

On Wednesday morning I walked dutifully to the pool and swam the mandatory 20 lengths in fairly cold water. We spent the evening of the New Year with my daughter in law's brother and family, and the second night of New Year with my family. Towards the end of that Thursday evening, I felt a familiar tickle in my throat signifying that I probably had 'a viral infection of the upper respiratory system'. I slept over ten hours that night which is very unusual for me; it was clear that I had no strength to go swimming on Friday morning. I barely had the strength to take the dog for her morning walk that I shortened considerably. Ditto yesterday evening and this morning.

So the swimming season finished more abruptly than I would have liked. With a little SQL mojo, here are the data for the past few years, since I started documenting each swim. In 2019, I was swimming only once a week, but since 2020 (the Covid year), I've been swimming twice a week.

YearNumber of swimsTotal lengths
201916438
202024562
202130548
202229567
202331561
202434710

My nose is running and my eyes are hurting, so I assume that's the end of this blog entry.



This day in history:

Blog #
Date
TitleTags
1605/10/2005
SudokoProgramming
20305/10/2009
Old computer in new caseComputer
41105/10/2011
Firebird DB management tool (3) - Bells and whistlesProgramming, SQL, dbExpress
97905/10/2016
DBA: Entering the final third of the doctorateDBA
107905/10/2017
A kibbutz dayJewish holidays, Obituary, Kibbutz
117805/10/2018
Geoff Emerick, 1945-2018Obituary, Beatles
134205/10/2020
Continuing the development of the Priority Cross Referencer (PrioXRef)Programming
142805/10/2021
Continuing the BP sagaHealth, CPAP, Blood pressure, Aldosterone
167205/10/2023
The neuroendocrine system and my blood testsHealth, Nutrition

Friday, October 04, 2024

Slow cooking with the barbecue mat

A few weeks ago, I wrote1 about ordering the 'Reusable Non-Stick BBQ Grill Mat' that finally arrived on Tuesday evening whilst I was rehearsing for the New Year Ceremony. I managed to extract the parcel from the kibbutz 'Post Office', and yesterday evening I had the chance to use it in the slow cooker. 

Below are two pictures: one with meat and mat, and one with vegetables on top of the mat.


The mat can be seen quite clearly in the picture without meat (left) and less clearly in the picture with vegetables - obviously because the vegetables are hiding the mat. It can be seen in the top right hand corner of the picture.

Did the mat do its job? Definitely yes! When cooking finished, I was able to scrape the cooked vegetables from the mat without them getting entangled with the meat. I then pulled the mat out of the slow cooker (this is slightly difficult as the sides of the cooker are very hot) then scooped the meat out. I think that next time I will add some string (or similar) to the mat to facilitate pulling it out of the cooker; if I put string on opposite sides then maybe I'll be able to pull the mat out with the vegetables still on it, making it even easier to decant them into a dish.

Both the meat and vegetables were cooked to perfection, so the mat had no effect on this, as I suspected. I think I know why sometimes one or two lumps of meat are cooked but are not tender: the amount of meat that I bought (1.6kg), when cut, filled out the bottom of the slow cooker, but had there been more meat, this would have been placed on top of the bottom layer and would not necessarily be covered in liquid. Something to bear in mind for next time.

Internal links
[1] 1817



This day in history:

Blog #
Date
TitleTags
41004/10/2011
Watching the weightDiet, Acupuncture
51404/10/2012
Blues for SJewish holidays, ClientDataSet, Reason, Slow cooker, Acupuncture
97804/10/2016
New Year greetingsJewish holidays
107804/10/2017
Another end to headachesHealth, Migraine

Wednesday, October 02, 2024

Apples, honey and missiles

For the past few years, the kibbutz has been holding its celebration of the Jewish New Year a day in advance, so allowing everybody to celebrate without inconveniencing those who will usher in the New Year with relatives, either on or off the kibbutz.

So yesterday I finished work just after 4:30 pm, took the dog for a short walk, showered (the day had been overcast but humid) then went down to the dining room to set up equipment and have a short rehearsal. The 'show' was due to start at 6:30 pm; we may have actually started on time (my phone was turned off so I couldn't check). The hall was filled with people of all ages - from new born children (they even got their own dance, whilst being held by one of their parents) to great grandparents.

 

Although it's very hard to tell, this picture comes from the final song of the evening, entitled with one of those Hebrew words that is very hard to translate; Google Translate says that it means "I wish", which is OK but doesn't go far enough in expressing the wish for a better outcome (or future, as you wish). I can tell, because if I magnify the picture, I can see a capo on the first fret. All the songs went down well and I'm sure that everybody present enjoyed themselves. 

We had just finished packing away the equipment when suddenly we were told - go to the air raid shelters! There are missiles from Iran in the air! I have to admit that the Iranians were very considerate: had they sent their missiles fifteen or twenty minutes earlier then our entire evening would have been ruined. Had they waited another ten minutes, then I would have been at home in our security room. Of course, the best option would have been no missiles at all. Not only that, Iran seems to have had a failure in intelligence: had they wanted to inconvenience the population, it would have been much better to send the missiles on the eve of the New Year (i.e. this evening) when half the population is traveling to the other half and so is out of the shelters, not on the evening before.

There are four (actually five) shelters within easy reach of the dining room so nobody panicked. I went down to the music shelter that I had left a minute or two earlier after having returned equipment; I was accompanied by maybe one hundred people. The air shelter is quite large, but having so many people meant that we had to stand. There is an air conditioner in the shelter so at least we had fresh air, but it was quite humid and I broke out into a sweat for the third time that day. At some stage, one of the teenagers asked if I wanted some water; he came back shortly after with a cup. Later on, the emergency crew came in with about twenty big bottles of water and someone gave me one, not that I drank that much.

Eventually (after maybe an hour) the all clear was given and we piled out of the shelter and into the fresh evening air. I went back into the dining room to pick up my guitar and pedal board and then it was home. There I found a few people in our bedroom that doubles as the security room watching the news. I received a brief update, and as the attack was over, went into the lounge where I could breathe some fresh air.

Here's a video of us accompanying the children who entered first grade a few weeks ago: each child is hold a placard with the name of the Hebrew month on one side and a picture of the season on the other. By chance, my second grand-daughter is standing in front of me.



This day in history:

Blog #Date TitleTags
40902/10/2011Holiday cookingCooking
134102/10/2020Weight at the beginning of OctoberHealth