Saturday, July 25, 2009

Better late than never - II

As someone wrote on a music mailing list, the fact that he likes no current music allows him the time and money to purchase and listen to all the music that he missed earlier. I've been following his advice, and bought two discs recently, both of which date from another era.

The earlier of the two is "The best of Peter Green's Fleetwood Mac". As can be inferred from the title, this collection is from the earliest line-ups of the band, before they went American. When Peter was in the band, they were basically a blues group, and most of the tracks on this disc are in this style. The exceptions - and the reasons why I bought the disc in the first place - are the singles: Albatross, Black magic woman, Green Manalishi, Man of the world, Oh Well. I never listened to the blues when I was growing up; this was a kind of music which hit its zenith in Britain in the mid-60s, and was already dying by 1969, approximately the year of this collection. I eschewed the blues changes as much as possible, a direction probably influenced by Richard Thompson, Robert Fripp, Peter Hammill, etc (in other words, all the musicians to whom I listened from 1970 onwards).

There is one track on this disc which reminded me slightly of Blodwyn Pig, who were, I suppose, a second generation blues band who were stretching the form by adding jazz and rock influences. BP were the first underground band that I saw, certainly the first that I liked and their's was the first rock record that I bought. It's interesting to note how much distance they had put between themselves and Fleetwood Mac, even though chronologically they were separated by a year at most. Maybe Peter Green too was heading out of the ghetto, an impression which is supported by "Oh Well"; unfortunately his health unravelled and he had to leave the band he founded before he could take it to new pastures.

Fast forward by two years, and it's time for Shirley Collins' "No Roses" album. I had a friend who bought the vinyl album at the time of its release, so I've been familiar with this for years. NR was Ashley Hutchings' first project after leaving Steeleye Span, and only two years after "Liege and Lief". What can I say? Not as groundbreaking as L&L, not as influential, but certainly more varied, and ultimately more interesting. Amazon were selling it for three pounds; the postage cost more than the disc. A hole in my collection and one which needed filling.

Serenpiditiously, this morning appeared an email on a music group listing about a Fairport Convention torrent from 1982. When I looked for this, I also found a television program about the Albion Band from 1979, featuring several snippets of interview with AH himself, several songs (including one with Shirley Collins) and a cut-up interview with Richard Thompson. I was back in the folk-rock belt, a place where I spent the seventies, and curiously, the nineties.

Saturday, July 11, 2009

Treeview program manager

A few weeks ago, I was looking at the screen of the computer belonging to the occupational psychologist for whom I write programs. The screen was extremely cluttered with icons, in main due to the fact that there were icons for each of the program suites which I have written for her. With ten different suites and two or three icons for each suite, that works out to a lot of icons! I idly suggested writing a program which would manage all of those programs, leaving her with only one icon on the screen. Basically, I was advocating a return to something similar to Windows 3.1 with its program groups.

There are several ways in which I could have implemented this; in the end, I chose an implementation based on the treeview control. This has four levels:
  • 0 - the top level, always 'programs'
  • 1 - the program group (for example, 'Adjectives')
  • 2 - the type of program (administrator, exam, results)
  • 3 - the path to the program
It would make things easier if I showed a screen shot of the program. Naturally, almost all of the data are in Hebrew, but as they say, one picture is worth a thousand words.

The program loaded its data via the treeview's 'loadfromfile' method, and of course saved its data with the 'savetofile' method. As opposed to most of my programs, this one has a sizeable main window (the treeview automatically resizes itself to fill the entire area), so I decided to add code which remembers the window's size. This data is stored in the registry. A neat hack.
// code to load a window's size from the registry
ini:= TRegIniFile.create ('\software\nbn');
with ini do
 begin
  self.Top:= ReadInteger (prog, 'Top', -1);
  self.Left:= ReadInteger (prog, 'Left', -1);
  self.Height:= ReadInteger (prog, 'Height', -1);
  self.Width:= ReadInteger (prog, 'Width', -1);
 end;
Yesterday, the psychologist asked whether nodes could appear in different colours; this would make it easier for her secretary to chose the correct programs. I did a certain amount of research via the Internet looking for Delphi code to do this, and it became apparent that I would need to use the 'CustomDrawItem' of the treeview. After a little more work, I discovered that I had already used this method in another program of mine and that it was very simple to do what was necessary. It's sometimes amazing to see how simple Windows code can be in Delphi which seems to be so complicated in other languages:
procedure TMainForm.TVCustomDrawItem(Sender: TCustomTreeView;
Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
 with sender.Canvas do
  begin
   case longint (node.data) of
    1: font.color:= clBlue;
    2: font.color:= clGreen;
    3: font.color:= clRed;
    0: font.color:= clBlack
   end;

  if node.level = 1
   then font.style:= [fsbold]
   else font.style:= []
  end
end;
After successfully displaying node text in colour, I had to decide how the colour of each node would be represented, and how this colour would be stored between invocations. At the moment, the program only recognises three colours (apart from black), and the index to these colours is stored in each node's 'data' property. The value can be chosen in the 'edit program' dialog box which appears whenever the user double clicks on a program title (level 2; double clicking on a node at level 3 will execute the program whose path is displayed). I quickly established that the 'savetofile' method does not save the 'data' property, meaning that I would have to write code to save the data, and then similar code to load the data.

The code to save the data was actually very simple: an ordinary text file is created, and for each node in the tree a line is written, consisting of the node's level, its text and its data, represented as an integer, where null data is equivalent to 0.
assignfile (f, datfilename);
rewrite (f);
for i:= 1 to tv.Items.count do
with tv.items[i-1] do
writeln (f, level, ',', text, ',', longint (data));
writeln (f, '*** end of file'); // dummy line
closefile (f);
Reading in the file and building the tree structure was a bit more difficult. The key to building the tree is to remember that the parent of a node at level 0 will be 'nil' - there is no parent, as this is the top node. The parent of a node at level 1 will be the node at level 0 (which will always be tv.items[0]). Following a node at level 1 will be its sons, and following each son node will be its son nodes; thus all that is necessary is to save a pointer to each node being inserted, and use this node as the father for the next nodes. Nodes at level 2 also have to have their colour restored.
datfilename:= extractfilepath (application.exename) + 'progman.dat';
assignfile (f, datfilename);
{$I-}
reset (f);
{$I+}
if ioresult = 0 then
 begin
  readln (f, line);
  while not eof (f) do
   begin
    i:= pos (',', line);
    level:= strtoint (copy (line, 1, i-1));
    line:= copy (line, i + 1, length (line) - i);
    i:= pos (',', line);
    case level of
     0: tv.Items.AddChild (nil, copy (line, 1, i-1));
     1: node:= tv.Items.AddChild (tv.items[0], copy (line, 1, i-1));
     2: pnode:= tv.Items.AddChildObject (node, copy (line, 1, i-1),
                                             pointer (strtoint (copy (line, i + 1, length (line) - i))));
     3: tv.Items.addchild (pnode, copy (line, 1, i-1));
   end;
   readln (f, line)
  end;
  closefile (f);
 end
  else
   try
    tv.loadfromfile (extractfilepath (application.exename) + 'progman.txt')
    except on e: exception do
    tv.Items.AddChild (nil, 'Programs');
  end;
There is a slight bug in this code: eof (end of file) will be true after the last line of the file has been read, but that line won't get processed because the code exits the loop. Instead of adding duplicate code to process that final line, I decided to add a dummy line to the file which will allow the final line of data to be processed whilst not being processed itself. This is called the 'sentinel' technique.

If the above code can't find its data file (which will be called 'progman.dat'), it looks for a file called 'progman.txt', which was the data file from the previous version of the program. If this file is found, then the tree will be built, albeit with no colours. If this file isn't found, then the program creates a new tree with a dummy first node.

Monday, June 29, 2009

Optimising Excel automation

The key to optimising automation is to call the automation server (ie Word or Excel) as little as possible. In Word, this especially means not calling the server inside a loop. This is normally achieved by building a string by concatenation in a loop, and then passing this string to Word when it is finally built, eg
tmp:= '';
with qExample do
 begin
  close;
  params[0].asinteger:= n;
  open;
  while not eof do
   begin
    tmp:= tmp + fieldbyname ('name').asstring + #13;
    next
  end
end;

wrdSel.typetext (tmp);

Optimisation should not be limited solely to outputting text. Looking over one of my programs, I discovered that I had output text to a table, and then set the shading for each cell in a column separately. It is, of course, better to shade a table column all in one go, and then set any cells which need to have differring shading. Bearing in mind what I wrote previously about accessing Word collections in Delphi, the code becomes
wrdTable:= WrdDoc.Tables.Add (wrdapp.selection.Range, row + 4, 12, 1, 2);
wrdTable.columns.item(1).select;
wrdSel.range.shading.texture:= 100;
// 'unshade' certain cells
wrdTable.cell (1, 1).range.shading.texture:= 0;
wrdTable.cell (2, 1).range.shading.texture:= 0;

And now onto Excel.... The major activity when automating Excel is passing values to the various cells; other activities such as formatting the worksheet exist, but are much less important. Remembering the prime directive in automation, "call the server as infrequently as possible", it seems as if there is little to be done to automate Excel. Well, not quite.

The first, most simple and most effective optimisation is to hide Excel until all the data has been transferred. Whilst it is cool to watch the data being transferred, the screen updating is also slow. So the standard code to launch Excel should be
XLApp:= CreateOleObject('Excel.Application');
XLApp.Visible:= false;
XLApp.Workbooks.Add;
Sheet:= XLApp.Workbooks[1].WorkSheets[1];
and at the end of the code should be the line
XLApp.Visible:= true;

How can the Word optimisation of concatenating the data before exporting it be translated to Excel? The answer is to declare an array in Delphi, populate this array in Delphi, and then transfer it in one go to Excel. I don't remember where I picked up this technique so unfortunately I can't credit anyone. [Edit: here is the link]

Here is the complete code from a demonstration program which I wrote for myself; the only component on the form is a 5X5 stringgrid renamed sg. The program populates this stringgrid and then performs the magic. The important stage is to chose a range in the worksheet which is the same size and dimensions as the array to be passed; this is the 'range:= ' line.
procedure TForm1.Button1Click(Sender: TObject);
var
 xls, sheet, Range, arrData: Variant;
 i, j: integer;

begin
 {create variant array where we'll copy our data}
 arrData:= VarArrayCreate([1, sg.RowCount, 1, sg.ColCount], varVariant);

 {fill array}
 for i:= 1 to sg.RowCount do
  for j:= 1 to sg.ColCount do
   arrData[i, j] := sg.Cells[j-1, i-1];

 {initialize an instance of Excel}
 xls:= CreateOLEObject('Excel.Application');
 xls.visible:= false;
 Sheet:= XLApp.Workbooks[1].WorkSheets[1];

 Range:= sheet.Range[sheet.Cells[1, 1], sheet.Cells[sg.rowcount, sg.colcount]];
 Range.Value:= arrData; {copy data from allocated variant array}
 xls.Visible:= True;
 range:= unassigned;
 sheet:= unassigned;
 xls:= unassigned;
 close
end;
Happy optimising!

Sunday, June 28, 2009

Yet more Word automation

Today I was checking out possibilities of optimising my Word automation code. I know that theoretically it's possible to send to Word a list of strings and then get Word to turn those strings into a table, but I tried this once and didn't succeed. The first stage is to build a long string, whilst separating the various values with a separator character, which is obviously one which won't appear in the text itself. I used the caret (^) as a separator, and one tells Word to use this character with the DefaultTableSeparator method. The code for the first step is thus:
wrdApp.defaulttableseparator:= '^';
tmp:= '';
with qJobs do // get text from this database table
 begin
  close;
  parambyname ('a').AsInteger:= a;
  parambyname ('b').AsInteger:= b;
  open;
  while not eof do
   begin
    tmp:= tmp + fieldbyname ('name').asstring + '^';
    next
  end
end;

tmp[length(tmp)]:= ' '; // remove the final caret
wrdSel.typetext (tmp);
The exported text will be in the form "a^b^c^d^e^f^g".

The key to turning this text into a table is to selected the exported text first and then turn it into a table. My normal method of exporting text into Word doesn't leave the text selected at the end, so I had to figure out how to select it. The exported text is always going to be a paragraph by itself, and it's always going to be the current paragraph (but not necessarily the final paragraph at the time of export). To quote an article which I found today, "if you want to know [the current paragraph number] in order to operate on the currently selected paragraph, table or other object, you can simply use: selection.collectionname (1)."
which in Delphi translates to "wrdSel.paragraphs.item(1).range.select". The Delphi "gotcha" is that Word collections have to be referred to as "collections.item(x)", not "collections(x)".

The next stage is turning the selected text into a table; this is done via the 'ConvertToTable' method, which takes a large number of parameters. Fortunately, we only need the first three; the first parameter says how to separate the table items, which is via the default separator character. The Word constant for this is "wdSeparateByDefaultListSeparator", whose value is 3. The second parameter is the number of rows in the table; although this parameter might seem to be important, it is irrelevant as the table adds rows according to the need, and so I pass the number 1. The third parameter, which is much more important, is the number of columns. Here is the code for this part:
wrdSel.paragraphs.item(1).range.select;
wrdSel.converttotable (3, 1, 2);

Normally I add a table to a Word document using this code:
wrdTable:= WrdDoc.Tables.Add (wrdSel.Range, 1, 12, 1, 2);
In the case of ConvertToTable, however, the table has already been created, and so the above line is useless. The solution is to access the current table which has been added to the document, not assuming that this is the first or last table. The way to do this is the same way in which I accessed the current paragraph, viz
wrdTab:= wrdSel.tables.item(1); // the new table

Once I have a pointer to the current table, I can do anything I want with it, such as removing the borders, shading one column, setting text alignment, etc.

Here is the final and complete code:
wrdApp.defaulttableseparator:= '^';
wrdSel:= wrdApp.selection;
tmp:= '';

with qJobs do
 begin
  close;
  parambyname ('a').AsInteger:= a;
  parambyname ('b').AsInteger:= b;
  open;
  while not eof do
   begin
    tmp:= tmp + fieldbyname ('name').asstring + '^';
    next
  end
end;

tmp[length(tmp)]:= ' ';
wrdSel.ParagraphFormat.Alignment:= 2;
wrdSel.typetext (tmp);
// the below line always selects the current paragraph
wrdSel.paragraphs.item(1).range.select;
wrdSel.converttotable (3, 1, 2);
wrdTab:= wrdSel.tables.item(1); // the new table
wrdTab.borders.insidelinestyle:= false; // no borders
wrdTab.borders.outsidelinestyle:= false;

wrdSel.homekey (wdStory); // jump to beginning of document
I used this code in a different place in the program to create a table with 75 rows of 5 columns each. The code executed almost instantaneously, as opposed to the length of time it took before. This technique cannot be slower than building the table manually, but the advantage becomes clearer the more rows and columns that have to be added.

Incidentally, I have one table with twelve columns and an changeable number of rows, but there is text only in the first and last columns. Of the other ten columns, only one will contain an 'X', which represents the numerical value for the first column in a range between one and ten. I did not use the 'ConvertToTable' technique to build this table as it is sparse, and the CTT technique should only be used for dense tables.

Next up is a similar conceptual technique for exporting data to Excel.

Tuesday, June 23, 2009

Counting beats with Van der Graaf

I had a long drive yesterday to the north of Israel, and took with me a few disks to listen to on the way. Coming back, I played Van der Graaf Generator's "Godbluff" and "Still life" and for want of something better to do, I started counting beats.

People have always ragged VdGG with their weird time signatures, but I've never really noticed and always assumed that those people were just tarring VdGG with the time signature brush, whether it's true or not. If you want real funny time signatures, listen to National Health....

So: "The undercover man" starts in 3/4 for two verses and then moves to 4/4 for the rest of the song. "Arrow" is in 4/4 throughout. "Scorched earth" is a different kettle of fish, though. The opening statement is three bars of 4/4 followed by one of 3/4. The verse has lines of 4/4 and lines of 5/4. The closing riff sequence is mainly in 5/4. There's a restatement of the riff at one point in 6/4. There's one section which seems to have 5/4, 3/4, 4/4 in successive bars. I also counted some bars as 7/4. That's fun for all the family.

Over the years, I haven't listened much to "The sleepwalkers", so this was quite a metric shock. I haven't figured out yet in which time signature the opening verse is - this will have to wait for another time. After the middle "if I only had time", there's a long riff section of 18 beats per repetition, which is probably three bars of 4/4 and one of 6/4. When the final verse comes in, the main structure of the vocal lines is 4/4, 2/4, 4/4, 4/4, with the instrumental bits in 3/4. I wonder whether the song was originally written like the final verse but became "tarted up" during the arranging process, during which beats were lost forever.

I listen to most of "Still life" quite frequently, but there are still some interesting things to be heard. The opening two tracks, "Pilgrims" and "Still life" seem to be totally in 4/4. Yesterday, I heard "La Rossa" as a slow 12/8, and on that basis, the middle section became very interesting ("if we made love now..."): a bar of 3/4 followed by two of 4/4, repeated several times (that could be written as 9/8 followed by two bars of 12/8, to make the triplet beat clear).

I noticed a few weeks ago that "My room" has a similar metric structure to the final verse of "Sleepwalkers" - 4/4, 2/4, 4/4, 4/4. If we take the first line, "Searching for" is a bar of 4/4, "diamonds" is a half bar of 2/4, "in a sulphur mine" is two bars of 4/4.

"Childlike faith in childhood's end" has most of its verses in 13/4 (or 4/4, 4/4, 5/4)!

Buy me a metronome for Christmas.

Monday, June 15, 2009

Better late than never

Several years ago I noticed that the more that I program, the less music I make. Unfortunately at the moment it's much easier to write computer programs than it is to write songs; for one, I am never short of ideas whereas I never have any ideas for the other.

But while I am writing programs, I am also listening to music, and in the 'better late than never' department, I must note my increasing interest for Joni Mitchell's classic "Blue" album from 1971. I've been listening to this album for the past few years but have never payed too much attention to it. At first, the faster songs ("All I want", "Carey", "California") served as my introduction, but lately their appeal has begun to fade, and to be honest, I almost wish that they weren't on the album, for I have fallen in love with the slower songs.

"Little green", "Blue" and "River" have become mainstays of my listening in the past few days. I admit that mainly I listen to the music and not to the words, not having enough spare processing power for them. The title song alone is worth the price of admission, especially in the 'middle bit', where a line sung in 4/4 suddenly compresses to be sung in 3/4 ("acid, booze and ass").

Tuesday, June 09, 2009

Inter-program communication

I wrote the other day about inter-program communication using a user-defined message which is broadcast to all windows. This technique is sound because only the programs involved recognise the user-defined message. Today I was reading about the wm_copydata message, which allows one program to pass data to another program. Here, broadcasting the message to all windows is strictly forbidden, as any program which has a wm_copydata handler will try to handle the message, sometimes with disastrous results. Here, the technique has to be to identify in advance (via the findwindow function) the receiving program. My use was limited only to passing a trigger: in plain English, 'when you receive this message, you'll know that I've finished'. If I ever use this technique in the future, I could pass a parameter in the 'wparam' field which has an agreed meaning. Here's a link to a program written in Delphi which uses wm_copydata. This isn't quite as robust as the one shown in the first link, although the article uses 'findwindow' and thus glosses over certain problems.

Saturday, June 06, 2009

More Word Automation

After figuring out how to add bold text to a Word document from a Delphi program, it was only a short step to figure out how to add underlined text. But the real cherry was finally figuring out how to add bulleted text, ie
  • line one
  • line two
This may appear to be trivial, but the code which Word creates when doing the above as a macro is fairly complicated. I found some code in a Japanese Delphi blog which basically copies the Word macro code and turns it in Delphi, but it didn't work for me. It turns out in the end that most of that code is actually unnecessary, and I managed to write a 'bullet' procedure with only a few lines of code. I am posting the code here in the hope that Google will bookmark it, so when some poor programmer tries to create bulleted text in Delphi, he will have an example which works. Here it is:
procedure wrdBullets (app: variant; const s: string);
{ This procedure MUST be passed the wrdApp object, as ListGalleries is a
property of this object, not of the selection object}
const
 wdBulletGallery = 1;
 wdListApplyToWholeList = 0;
 wdWord10ListBehavior = 2;

var
 template, sel: variant;

begin
 Template:= App.ListGalleries.Item(wdBulletGallery).ListTemplates.Item(1);
 Sel:= App.selection;
 Sel.Range.ListFormat.ApplyListTemplate
(Template, false, wdListApplyToWholeList, wdWord10ListBehavior);

 Sel.typetext (s);
 sel.Range.ListFormat.RemoveNumbers;
end;
As always, the problem with creating Word automation code (in any language, but especially Delphi, which is a non-Microsoft language with a different syntax) is knowing which methods belong to which objects.

I have created a Delphi unit which exports all the more complicated functionality, such as bold, underline, bullet and gotobookmark. This way, I only have to write the complicated code once, and then every program which I write can use this code simply by including a call to a procedure.

Encouraged by these achievements, I also included the unit a procedure which receives a range of Excel cells and puts around and in them a double border.

Friday, June 05, 2009

Exam program launcher

I work with my occupational psychologist friend every Friday morning. Today, whilst we were talking about a program that we are developing, I noticed that her computer's desktop was filled with icons from my programs: normally three icons per program (one manager, one exam and one results program). I suggested writing a 'program launcher' program, which would present the user with a choice of programs to be run, and then run the chosen program. This would cut down the number of desktop icons drastically.

In the end, we decided against such a program (or rather, left it as an optional exercise for the future) as it doesn't improve the working environment. But out of this idea came a better idea: write a program launcher for the various exams that clients sit. What, you might ask? If a program launcher for multiple programs was discarded, why write one for only a subset of those programs?

The difference is this: a client will sit down at one of the computers in the computer room. The secretary will bring up one of the exams, fill in the client's details (surname, forename, id number, etc) then let the client do the exam. When the exam finishes, the secretary will bring up another exam, type in the client's details again and then let the client do this exam. All the exams were written purposely as standalone programs which need no external files and so can be distributed and run at other sites with a minimum of effort. This is why the user's personal details have to be entered into every exam. Use of a launcher program would mean that the personal details need only be entered once, and then the user can get on with all the exams.

The exam program launcher which I designed works on the following basis:
  1. The user's personal data are entered into the launcher
  2. The launcher saves these data in the computer's registry
  3. A list of exams which the user will sit are chosen from the list of all exams (this list comes from a database which holds the exam's name, its location and any command line parameters)
  4. The first exam in the chosen list is removed from the list and run
  5. When the exam finishes, it notifies the program launcher than it has finished
  6. If there are more exams in the chosen list then the launcher loops to step 5
  7. The launcher removes the data saved from the registry and exits
Whilst the above isn't exactly rocket science, one will not find many examples of how to do inter-program communication in Windows. In DOS, this probably would have been done with an interrupt; in fact, during the dying days of DOS, I became quite adept at writing TSR programs, initially in assembly language and then in Turbo Pascal, which used the $2F multiplex interrupt for this purpose. But now we are in Windows, and so we must use a message. But which message?

It turns out that Windows has the 'RegisterWindowMessage' function which creates a unique message number for the parameter passed to it. Several programs can call the same function with the same string; the first program that calls the function will create (register) the message number, and every subsequent call will retrieve the same number.

I modified some of the exams to do the following:
  • In the 'GetDetails' dialog form, the program checks the registry to see whether it has values. If the exam has been called from the program launcher, then there will be values, whereas if the exam is run directly then there won't be any values.
  • When the exam finishes, it makes a call to the RegisterWindowMessage to get the special message number and then broadcasts a message to all the running programs with that message number.
If the exam is run directly, nothing untoward happens, but if the exam is run from the launcher, then it will indeed notify the launcher when it completes. The launcher handles this completion message by executing the next exam in the exam list.

It's a bit difficult to explain this asynchronous, inter-program communication in a synchronous manner (ie writing this explanation) and to someone who isn't used to event-based programming, but like every clever idea, it is in fact quite simple (especially in retrospect). One of the neat things about this program is that the exams can also be run directly, with no side effects.

Friday, May 29, 2009

Who plays with whom?

In 1996, I started writing a cd database program; this would have been one of my first serious efforts at writing database programs in Delphi. The program would store physical information about musical cds (the cd's internal id number, how many tracks, how long each track lasts), and I would add data such as cd name, song title, who made the cd, who played, who composed, etc. Once this was done, I could get out of the database arcane facts such as how many tracks were composed by Richard Thompson, on how many tracks Simon Nicol played and so on. Maybe the highlight of the data mining was calculating on which tracks both RT and SN played.

Under the hood, the program was not written very well. My original source of information about Delphi database programming used the 'ttable' component all the time, so tables were lavishly scattered around the code. Somewhere around 2003, I started replacing tables with queries, at least in the places where I understood what was happening. Such queries made the code much simpler to understand, with a few terse but clear lines replacing such monstrosities as a record by record search of a table.

The 'who plays with whom' form was an exceptional case of using tables, most of them temporary, and presumably because of this form's complexity, I had stayed away from trying to improve it. Yesterday, I was having a routine look through the program, changing bits and pieces to current standards, when I came across this form. I knew what the code was supposed to do, but didn't like the way it was being done - and in fact, I cringe at what I had written twelve or thirteen years ago. Well, I didn't know better.

As it was dog walking time, I had the perfect opportunity to think about improving this function. I was fairly sure that I could replace almost all of the code (some of which dealt with finding which songs were common to two or more people, and some of which dealt with finding data about those songs from various tables - as if I had never heard of the 'join' statement in SQL) with one moderately advanced query. When I came back from our half hour walk, I set to work, and came up with this:

select tracks."track name", artists."artist name", cds."cd title", tracks."track id", count(*)
from musician, tracks, artists, cds
where musician."track id" = tracks."track id"
and tracks."artist id" = artists.id
and tracks."cd id" = cds."cd id"
and musician."person id" in (0)
group by tracks."track name", artists."artist name", cds."cd title", tracks."track id"
having count(*) = :an

The '(0)' gets replaced by a list of people (their id numbers), so basically the query returns a list of tracks (their name, the artist and the cd on which they appear) and how many times those tracks were returned from the musicians' table, given a list of musicians. The musicians table, being a link table between people and tracks, has three fields: a meaningless id, a person id and a track id, where each record means that person 'a' played on track 'b'. In retrospect, the record's id field is unnecessary but harmless; but I have refrained from changing the database structure as this would entail multiple changes in the program code.

The parameter 'an' is the number of people contained in the list passed to the query. The query returns a list of all the songs on which one or more people in the musicians' list played, but the count value can vary: it could be one (in which case only one of the people in the list played on the song), two or more. This value can only be checked after the query has completed, which is why the final line is a 'having' clause, something which I rarely use. The query must return only the songs on which all the people played, so the 'count' field must equal the number of people in the list.

Thursday, May 28, 2009

Holy Grail, part 2

It turns out that the holy grail is not so easily found.

The code which I posted here worked perfectly at work. So when I came home, I plugged it into a real program ... and of course, there was no bold text. Hmmm, I thought to myself, maybe I've written this code before and discarded it because it doesn't work on my computer. Maybe it will work on my client's computer, which is the main point.

Then I took the dog for a walk. Whilst on the walk, I thought about this 'bold' problem, and decided that I should attack it with a more scientific approach. First see whether the demo program does print bold text on my computer - after all, I'm using the same version of Word (2003), and the code which I saw on the internet was several years old, meaning that the functionality has existed from much earlier versions of Word.

So when I came home, I ran the original demo ... and got bold text. I then took the specific code for the bold text and plugged it into my program. Still bold text. I replaced the literal string being printed bold with what I actually want as bold - no bold. Aha! The moment of revelation! Could it be that this code prints English in bold text, but not Hebrew? This wouldn't be the first time that I've seen a gotcha with Hebrew text. As I say, maybe I have been here before and discarded the (almost) correct solution because it didn't print Hebrew text in bold. This time, I knew that the code was almost correct, and this gave me the impetus to carry on.

The next step was to see what Word does itself, by recording a macro and performing the required operation, then by looking at the resulting macro. I often do this, but sometimes there's a problem recognising the variable which Word uses for the operation. Anyway, this time I could see what was going on: first, there was the call to set the value of the 'bold' property, and then there was an extra call, setting the value of a property called boldbi (presumably a contraction of 'bold bidi', ie bidirectional, where 'bidi' is often used in Windows when printing right to left languages such as Hebrew and Arabic). Based on this knowledge, I then added a call in my program to set the value of this new property - and then my Hebrew text appeared in bold! So here is the complete 'bold' procedure:
Procedure Bold (const s: string);
begin
 wrdSel.Font.Bold:= 1;
 wrdSel.Font.BoldBi:= 1;
 wrdSel.TypeText (s);
 wrdSel.Font.Bold:= 0;
 wrdSel.Font.BoldBi:= 0;
end;

wrdSel is a variable global to this procedure, declared as per yesterday:
wrdSel:= wrdApp.selection.

Wednesday, May 27, 2009

Holy grail found

Every now and then, a 'programming holy grail' emerges: this is normally something that I very much want to learn how to do in a program, but can't find out how to do it. Such a holy grail has to be something incidental - otherwise the program's development will be stymied and never completed. The latest holy grail is something which has been bothering me for some time - how to print from a Delphi program bold text within a Word document.

Creating a Word document and adding contents to it is called automation; whilst there is a fair amount of information about this on the internet, it tends to be basic, such as how to create the document, add some text and then close it. The finer points of formatting tend not to be discussed in internet articles. As it happens, I bought several months ago an excellent book on Office Automation, "Microsoft Office Automation with Visual FoxPro"; the only problem with this book being that it's written for Visual FoxPro (duh!) whereas I'm using Delphi, and the syntax is different. It's not too different, but sometimes sufficiently so and not clear enough which object has which properties and methods.

To create a Word document, and some text and then close it is done like this in Delphi:
var
 wrdApp, wrdDoc: variant;

begin
 wrdApp:= CreateOleObject ('Word.Application');
 wrdApp.visible:= false;
 wrdDoc:= wrdApp.documents.add;
 wrdDoc.select;

 wrdApp.selection.typetext ('Wow - I''m adding text to a Word document from  Delphi');
 wrdApp.selection.typeparagraph;

 wrdApp.visible:= true;
 wrdDoc:= unassigned;
 wrdApp:= unassigned;
end;
All calls to the wrdApp object are slow, and so I've found all sorts of ways to improve the duo of typetext and typeparagraph; the latter call is unnecessary, as appending a carriage return (character 13) to the string being printed is enough to force the cursor to a new line. When printing blocks of text, I now add the text to a wide string variable, and only pass that variable to the typetext method when all the text has been added.

So how does one add bold text? As it turns out, it's very simple, although this method negates the above optimisation. First of all, I define a new variant, wrdSel:
wrdSel:= wrdApp.selection;
This by itself simplifies the typing of the original code, for now I can replace wrdApp.selection with wrdSel, thus saving 10 characters each time. This may not seem important, but it does save wear and tear of the fingers. The 'selection' object has a 'font' record which holds a 'bold' property, and in order to print bold, all one has to do is turn this property on (and afterwards turn it off). So the simple code now becomes
var
 wrdApp, wrdDoc, wrdSel: variant;

begin
 wrdApp:= CreateOleObject ('Word.Application');
 wrdApp.visible:= false;
 wrdDoc:= wrdApp.documents.add;
 wrdDoc.select;
 wrdSel:= wrdApp.selection;

 wrdSel.typetext ('Wow - I''m adding ');
 wrdSel.font.bold:= integer (true);
 wrdSel.typetext ('bold');
 wrdSel.font.bold:= integer (false);
 wrdSel.typetext (' text to a Word document from Delphi' + #13);

 wrdApp.visible:= true;
 wrdSel:= unassigned;
 wrdDoc:= unassigned;
 wrdApp:= unassigned;
end;
Fortunately, I doubt whether I'll be using bold text very much in the body of a print-out; it's more likely that a paragraph heading will be in bold and the paragraph body in regular text. In this case, all the body can be loaded into one string and then printed.

I don't know how long I've been searching for this holy grail, but I'm pleased now that I've found it. I imagine that soon there'll be a new grail to search for.

Tuesday, May 05, 2009

Heron

My previous entry, a ten year old review of Heron's "River of fortune", seemed to come out of nowhere. What inspired it was receiving a double cd of all the tracks that Heron recorded for Pye's "progressive" label Dawn in the early 70s.

I made reference to the "penny tour" - this took place at the beginning of November 1970. Reasoning that for the admission price of one old penny there was no way that I could lose, I went to the concert. I know that four groups appeared, but two obviously made no impression as I could never remember who they were. One of the two groups that I do remember was Comus, who might have had Lindsay Cooper (later to play with National Health and Henry Cow) in their ranks at that time. The other group was Heron.

So impressed was I with their performance that I immediately went and bought their eponymous album. If I remember correctly, this was via mail from some pre-Virgin outfit which offered discounts, so I have no way of knowing whether the album actually hit the shops.

The simple arrangements - the record was recorded "live" in a field, supposedly with no overdubs - caught and tickled my ear, and some of the songs accompanied me throughout the years. Unfortunately, an accident with a record player managed to leave the opening song on side two, "Lord and Master", with a huge scratch, making it unplayable.

At the beginning of the 90s, in a fit of nostalgia, I discovered that there had been a cd release for Heron called "The best of ... plus", which featured songs from both of their albums as well as a few oddities. Whilst the cd was better than nothing, it wasn't really what I wanted, which was the first album in its entirety, sequenced as per the vinyl. "River of fortune" was interesting in its exhumation of the old songs, but sounded too modern for the material.

So I was delighted when I found the new compilation, which begins with the first album in toto. Here's the sole review at Amazon, which sums things up nicely: I can't believe how good this album is - imagine if in the summer 1970 Simon and Garfunkel, the Beatles and Crowded House (no kidding) got together and recorded a hazy summer filled acoustic album in a field complete with heavenly vocal harmonics and real background bird song and field sounds - this album is that and more. You will NOT be disappointed, a true gem of a find; how this band didn't end up massive is a true mystery to me. I'll put money on it being your new fave album within 10 minutes of listening.

Apart from the delight of being reunited with old friends who haven't lost their charm, it's also very instructive to compare the songs from the first album with their other material. Most fascinating from my point of view is "Harlequin 2 (long version)", an alternate take of this exquisite song. I remember from the penny concert when Gerald Moore introduced it by saying that "harlequin is such a lovely word" - so much so that he wrote several numbered Harlequin songs. After the song part of H2 ends, there is a brief pause and then a longish (by Heron standards) instrumental section, followed by variations on the song's chorus. The instrumental section is adorned by several instruments being played out of time, explaining why the song was cut at the end of the vocal section and so appeared enigmatic. I wrote that this is an alternate take; it may be the same basic take as the final version, but mixed slightly differently and without the Hammond organ overdubs, which give the lie to the "recorded live in a field" statement. Actually, H2 is the only song which sounds as if overdubs were added.

I am reminded of the quote which I put in the 'River of fortune' review: we shortened a couple of the original recordings because of cock-ups. The out of time instrumental contributions certainly fall into the cock-up category.

There is another extra song, 'Rosalind', which sounds like the other songs on the first album. In my review I compared Heron to Crosby, Stills and Nash, but this song sounds much more like Simon and Garfunkel. In fact, listening to the entire first album reveals a division in sound between two camps: on the one hand there are Roy Apps and Tony Pook, whereas on the other hand there is Gerald Moore. The Apps/Pook songs have two part harmonies and are gentle (for example, "Car crash", "For you"), whereas the Moore songs tend to be more adventurous musically (the aforementioned "Harlequin 2") and sound closer to pop. The best songs, naturally, are when everybody contributes fully.

This division becomes clearer on the second album, when bass and drums were added to the Heron mix (and I can also hear electric guitar here and there). All the charm of the first album has been lost, replaced by some homogenous acoustic pop sound which is not particularly good. I have only listened to a few of the songs from the second album; those which included on the 'Best of' compilation are similar but not as good as those from the first album, and those which weren't included are dissimilar and nowhere as good.

It seems very much like I am going to confine my listening to the first album and recommend it to everyone.

Saturday, May 02, 2009

Heron - River of fortune

Below is a review which I wrote of the above disc ten years ago; it appeared on the online review magazine, The Greenman Review, but is no longer available there.

------------------------------------------------------------------------------------

If your taste runs to obscure British groups, then you're in luck as you can't get more obscure than Heron. Originating in a Maidenhead folk club in 1968, this group (Tony Pook - vocals; Roy Apps - guitar, vocals; Steve Jones - accordion, piano; Gerald Moore - guitar, mandolin, vocals) achieved a certain amount of notoriety by eschewing the recording studios and making their first eponymous record in a field. In an attempt to publicise their record, they took part in what was known as the 'Penny tour' - the price of admission to the concerts being one (old) British penny. Unfortunately, Heron the record didn't sell vast amounts in the record shops, and although a second album ("Twice as nice at half the price" - also recorded in a field) was released, this too was a commercial failure, and so the group split up - 25 years ago [1972].

Further on down the road in 1997, the "boys" decided to get together again (without Gerald Moore, who had found some success with GT Moore and his reggae guitars, but with Gerry Power), and "River of Fortune" is the result. Of the sixteen songs on this disk (70 minutes long), eight are rerecordings of songs which originally appeared on their first album, three are rerecordings of songs from their second album, and five are new recordings of songs which date from that era but didn't appear on any of their records.

And now to the music: Heron, in my humble opinion, could have been the British answer to Crosby, Stills and Nash: acoustic guitars, a bit of organ or accordion, and sometimes three piece harmonies.

The songs are simpler, less "arty" and more "down to home" than CSN, being generally less pretentious (although Gerald Moore's lyrics tend to the abstract). In keeping with the Heron tradition, ROF was recorded in a field, but the production is much more modern, with reverb added to the vocals and instrumental overdubs added at will. While this intially detracts from the special feel of their earlier albums, it allows one to listen to ROF without an anachronistic feeling.

The songs are pleasant and melodic, well-played without being over-arranged, and there is plenty of variation. Whereas first time around, the songs were short (only one song on their first album was over three minutes long), on this one they are much longer. As Steve Jones wrote to me, "With regard [to] lengthening songs, has it occurred to you that the earlier versions may have been shortened? One of the reasons for including some of the songs was so that we could do them the way they were originally intended (more to the point, we shortened a couple of the original recordings because of cock-ups: it's the problem with spontaneous recording)."

An example of this is Moore's "Harlequin 2", one of my favourites from the original album; here the verses are arranged much as they were, but there are long instrumental interludes, which weren't present on the original. The title song starts off with a burst of Spanish guitar and some moody synthesizer, but then moves into a jolly singalong for the chorus; this track is also extended and features a live-sounding vocal coda.

The general sound of the disk is much fuller, this being due to the influence of Steve Jones the keyboardist, who also served as producer. Obviously he was quite limited 30 years ago as to which instruments he could take into a field, but also the range, variety, quality and usage of keyboard instruments has changed greatly during that time.

The complete track list is as follows: "Car crash", "Lord and Master", "River of fortune", "Wanderer", "Yellow roses", "Stars", "Friend", "I wouldn't mind", "Harlequin 5", "Adagio", "Carnival and penitence", "Summer in the city", "Harlequin 2", "Upon reflection", "Smiling ladies", "Gypsy trails".

Sunday, April 26, 2009

The iron law of bureaucracy

I recently came across the curious case of Mr X, whose job is similar to mine: he supports an ERP database, although his db is more financially orientated whereas mine is more production orientated. Mr X and I have worked before at a previous company and with a different program. I inherited the ERP database that he had helped to construct and supposedly maintain. Over the course of several years, I simplified and streamlined the procedures which use the database, making life much easier for its users.

Mr X was initially employed by a senior manager, a champion of Mr X, for a six month period, during which he was supposed to move his company from one ERP program to another. Apparently, once that period was up, he claimed that his work had not yet finished. After sixteen months, he is still there. It appears that Mr X has utilised a variant of Pournelle's Iron Law of Bureacracy (see below) to ensure that he never leaves; instead of using his energy to improve conditions for users, he uses his energy to keep his job (something very important in these difficult economic times).

Apparently he had kept all administrative powers to himself, so much so that a user (who might be described as a power user) has to ask Mr X to open accounts for new customers. I, on the other hand, have little interest in being so involved in day to day operations and gladly give to certified users the privilege (pun intended) of opening accounts. If I spent all day doing that, then I would have no time to spend on doing interesting things, such as developing and training.

According to this power user, an outside person was called in the other day to examine the database and make recommendations. Things would never have got to this level had there not been a change in management, in which Mr X's champion left the company.

Pournelle's Iron Law of Bureaucracy states that in any bureaucratic organization there will be two kinds of people: those who work to further the actual goals of the organization, and those who work for the organization itself. Examples in education would be teachers who work and sacrifice to teach children, vs. union representative who work to protect any teacher including the most incompetent. The Iron Law states that in all cases, the second type of person will always gain control of the organization, and will always write the rules under which the organization functions.

Knowledge hoarding

Consider the case of Ms X. She has been working in the company long enough to look at a customer order and instinctively know what raw materials have to be ordered. As she doesn't trust the stock levels of raw materials being reported by the ERP program, she goes onto the factory floor and performs her own stock taking. She then figures out how much she has to order, based on what there is and what is needed, and then places that order. Some people would consider Ms X a very important worker, but I would consider her a very dangerous person.

She is an example of a knowledge hoarder, someone who keeps her albeit important knowledge of company procedure and needs to herself instead of sharing. What happens when Ms X is ill or on holiday? Does the factory stop producing because no one knows what to order? And in fact, no one knows what to order because Ms X, intentionally or otherwise, has sabotaged the ERP database by not entering essential information into it. And what happens should the order be changed (as frequently happens) after Ms X has seen it? The purchase order becomes out of date, and items that we don't need get ordered (and probably items that we do need don't get ordered), thus building up stocks of materials which possibly will never be used.

Not surprisingly, Ms X opposes all the suggestions for change that I have made. She says that she can't trust the ERP program and that the procedures necessary are too time involving. Whilst it's true that the ERP program has to have complete and correct data in order to work properly, it is possible to make small changes in procedure which will improve matters. If today we make a 10% improvement, and tomorrow we make another 10% improvement, then we are well on the way to making a 100% improvement, whereas if we make no improvement today, we will never make any improvement.

I can see two possible reasons for Ms X's behaviour, one conscious and one subconscious. The conscious reason is that as long as she hoards the necessary knowledge, she can't be fired. Should management be foolish enough to fire her because of her knowledge hoarding, then the company would be in a worse position that it is now, because then nobody would know what to do. Apparently, she uses this power as a stick with which to improve her conditions of employment, but I don't know about this personally.

The subconscious reason is due to fear of change. According to "One small step can change your life", a structure within the brain called the amygdala is responsible for this fear. The amgydala is absolutely crucial to our survival. It controls the fight-or-flight response, an alarm mechanism that we share with all other mammals. It was designed to alert parts of the body for action in the face of immediate danger. One way it accomplishes this is to slow down or stop other functions such as rational and creative thinking that could interfere with the physical ability to run or fight (Dr Robert Maurer, pp 23-4). Dr Maurer's solution to this automatic response is to make small changes, those which don't "set off" the amygdala's alarm.

Frequently making such small changes allows the brain to create new neural connections, which eventually enable the person to internalise the behaviour required. From my point of view, the small changes which are made help improve the ERP database.

This is why I'm not looking for comprehensive plans which change overnight everything that we do. Rather, I look for small procedures which can be changed and improved without raising anybody's hackles. Such procedures stand a much better chance of being implemented, and when they do, they make the possibility of further changes easier.

I'm not sure how I'm going to overcome Ms X's unwillingness to change/share her knowledge, but I do know that I'm going to raise the subject with her superiors.

Saturday, April 25, 2009

Positive changes

Change is best absorbed into the corporate culture when its impetus comes from field workers, not from senior management.

A few weeks ago, I was approached by a mid-level manager to help with quality control reports. She holds a monthly meeting in which cases of poor quality control are discussed; the basis for the meeting is a spreadsheet (to mangle a quote from the 60s, whenever I hear the word 'Excel', I reach for my pistol*) in which people have manually entered the order details, the problem and the solution. This spreadsheet is accessed by many people, which makes it an IT nightmare, as well as difficult for those people to access, update and retrieve.

My immediate solution was to create a sub-form (and an underlying table) connected to the lines in a customer order, where the various data could be added. We discussed which fields were needed - person name, department, problem and solution - and the actual implementation in the ERP program was finished within an hour. As a form on its own is normally of little use, I also wrote a report which would extract the data from the orders and display it in useful form.

As the Passover holiday intervened almost immediately after doing this, I forgot about it. When I returned to work after the holiday, I checked a table which I keep for my own benefit in which I list all the forms and reports which I have developed (this helps when someone phones and wants help with 'their' report - as if I remember what they're talking about), in order to see what I was working on prior to the holiday. Coming across this table and its report, I decided to check how much data had been added. I was pleasantly surprised to see that in the short time that the form had existed, it had been used by several people.

Of course, I wasn't too pleased that there were so many lines, because each line means a fault with a product, but from the IT point of view, more lines means more use, which means that the change has been absorbed.

I wasn't involved in disseminating the fact of this form's existence - the manager who asked for the development had seen to that - so I was pleasantly surprised on Thursday when someone called and asked how to use the form, someone whose need to use that form would not have immediately occurred to either me or the manager.

If people in the front lines need an improvement or addition to the ERP program, then that change will generally be embraced enthusiastically, because they can see the immediate value of the change. They also feel ownership of that change, a feeling which gives them a responsibility to make sure that other people utilise that change.

When announcements of change come from senior management, they tend to be perceived as yet another piece of interference from above, something designed to make our job harder, something irrelevant to our needs and something which is divorced from our experience. Part of my job is how to make such changes relevant to field workers, how to make them embrace these changes, help them identify with the change and take ownership. This is not an easy task.

Next episode: knowledge hoarding and the resistance to change.

* After writing this, I discovered that the original quote comes from a play written by Hanns Johst and is normally attributed to Nazi leader Hermann Goring ("when I hear the word 'culture', I reach for my gun"). Oh dear, I never thought that I would be quoting Nazis.

Tuesday, April 21, 2009

Dangerous ideas

I should point out at the beginning that I don't like change very much in my personal life, and was quite outspoken in my distaste of the huge changes that my kibbutz has undergone in the past few years. Putting this simply, I "signed up" to live on a kibbutz on the understanding that such and such were the rules; in the past few years, those basic rules have been changed (often to their opposites) and so at times I feel that I have been lied to. But I'm won't be writing about such long reaching and personal changes here.

At work I've been developing recently what some might define as revolutionary or even dangerous ideas. Such an idea changes the current method of operation in some area, hopefully for the better. These ideas are considered dangerous because they change the status quo and cause people to realign themselves. Such ideas are generally designed to improve management's control and understanding of the business procedures, to make life easier for those implementing the procedures, and like all good doctors, to do no harm.

Not all of my ideas stick, but there have been some very good ones over the years which helped my company perform better (or at least improve management's knowledge of how well we were performing).

As I have probably written before, at the end of 2006, my company was merged with another company. Both companies make office furniture; we make chairs, they make desks and cupboards. We had the same owners.

Until the end of 2008, both companies were run almost autonomously, especially in my field of IT, and there was very little knowledge passing between them. 'My' company had its business practices, and 'they' had theirs. This was often a source of frustration to me, as I could see places where I could improve their practices, but there were high placed managers who refused all changes, and generally tended to demean and even insult me, normally claiming that I didn't understand anything and that 'my' company was doing everything wrong. There were one or two practices in the other company which were suitable for implementation in 'my' company, and these were accepted almost immediately.

This year, for various reasons, those managers are no longer working in the company, and suddenly I find myself in great demand to improve the business practices of the other company. Not only is nobody opposing me, I am being encouraged to change and improve. When one considers the fact that a year ago I was on the verge of leaving, one can see what a huge change has occurred.

Most of the changes that I have suggested are practices which are being used daily by 'my' company, and have been in use one way or another for several years, so they are tried and tested. Sometimes it's easy for me to think of these changes, and sometimes hard, but one thing is clear: the development of these ideas tends to be very fast, whereas the implementation tends to be very slow.

The implementation is slow because other people have to absorb the ideas; they have to overcome their natural opposition to change, and they have to accept the ideas. As they are Israelis, they also have to argue about them. I wrote three years ago about change, and am aware that certain changes have to be performed slowly and in small steps. Unfortunately, changes in usage of ERP programs require that everything is changed at once; if one changes only a little bit, it can be worse than not changing.

At the moment, I am feeling a little like Copernicus or Galileo. In the 'old' days, astronomers constructed a cosmology in which Earth was the centre of the universe. In order to support this cosmology, astronomers were forced to make more and more special cases, or obfuscate the theory in order to allow observation to match theory. Occam's razor had yet to be invented. Copernicus showed how a helioconcentric cosmology better matched observation with a simpler theory.

Coming down to Earth, the 'other' company constructed a series of operations in order to support the admittedly complicated processes of production extant in their factory. On the basis of these operations, more esoteric operations were added, and so on. None of these operations are supported in the 'native' version of our ERP program, but because the program is extensible, extensions were created. These extensions became part and parcel of their way of life, and became even the focus for 'religious' wars (well, arguments).

Then I come along, and say "if we do such and such, then we can accomplish the same thing with less effort and better use of the native ERP functions". As people have become so attached to their way of doing things, it is exceedingly difficult for them to understand, let alone embrace, what I am suggesting.

Tuesday, April 14, 2009

Thermomineral baths

It's holiday time here in Israel. Hundreds of thousands of people have taken to the countryside, either walking or simply having picnics on any available stretch of grass. I've been at home most of the time, but yesterday I decided to take the family to the thermomineral baths at a place called Hamei Yo'av, which is about 50 minutes drive from here. As I said, why bother going to the Czech Republic (I had this dream of traveling to Karlovy Vary, aka Carlsbad, for the spa) when we can travel only 50 kilometres to get the same treatment.

The online brochure for the springs here shows plenty of nubile young women, but they were in short supply yesterday. The baths were filled mainly with older people, but of course one doesn't go to these baths to see and to be seen, but rather to relax in the warm (37-39 degrees Centigrade) water. We might have been given contrary information: on my previous visit to the spa several years ago, I had been told that one should not stay in the water for more than 30 minutes, whereas my wife was told that 10 minutes was the limit. I stayed in for about 25 minutes and would have been content to stay for longer. The water is not only warm but also contains various minerals; the smell of sulphur is quite strong when one approaches the baths, but being almost anosmic, it doesn't bother me too much.

After the baths, we went the whole hog and had a relaxing 45 minute massage (booked the day previously; we didn't want to take the chance of turning up to find that the masseurs were fully booked). I've never had this kind of massage before; the only massages which I have had have been of the therapeutic kind in which one's muscles are pulled, prodded and stretched in ways that nature never intended. Yesterday was a much gentler experience, from head to toe. My wife very much enjoys this sort of thing (she had a massage in Eilat when we were there a few years ago when my son and I went scuba diving) whereas on yesterday's basis, I can take it or leave it. The massage and baths were certainly relaxing: I was a bit worried about driving home, and both she and I were falling asleep by 8pm.

Monday, April 06, 2009

Dave Stewart/Barbara Gaskin - Green and Blue

There comes an awkward point in the interaction between any musical artist and myself when I find that I don't like the latest offering/creation of the artist.

The first time that this happened was in 1973, when Peter Hammill released "Chameleon in the shadow of the night". Until then, Peter and Van der Graaf could do no wrong, although I admit there were parts of 'Lighthouse keepers' which were on the edge. "Chameleon" was a slap in the face, a complete change of style, and it took me both by surprise and by undelight. At the time, I was in correspondence with Mr Hammill, and it took all the courage I could muster to say that I didn't like the album. When pressed, I said that it was too empty, and Peter wrote back saying that's what he was capable of now, it's no longer a band but me solo. In time, I began to appreciate and cherish the album, but again Mr Hammill and I parted company a few years later, around the time of "pH7". The songs simply did not interest me. In fact, although I've bought a few solo albums since that time, as well as the two new Van der Graaf disks, nothing has caught my fancy very much.

I've written previously about how Jackson Browne fell out of favour, primarily by staying the same, by not increasing his harmonic vocabulary and by writing songs that didn't interest me. Or maybe it was me that was changing.

The great Richard Thompson is also facing decreased purchases by me, although maybe I am being unfair to him in that his material is taking longer and longer to sink in. Randy Newman hasn't passed the watermark yet, although I had my doubts about "Bad Love" and "Faust". Kate Bush after "The hounds of love" also falls into this category, although I hadn't been totally gung-ho about all her previous output either.

And now we come to Dave Stewart and Barbara Gaskin. Dave was the organist in "Hatfield and the North", "National Health" and "Bruford", groups who produced albums which occupied a lot of my listening time from 1975 onwards. After the dissolution of "Bruford" in around 1981, Dave turned his attention to making intelligent pop singles (is that an oxymoron?) but still managed to disappear off the radio. Barbara was one of the three Northettes who contributed eerie vocals to the Hatfield canon, and then apparently linked up with Dave in real life.

Their 1991 album, "Spin", was like a breath of fresh air, with intelligent music and witty lyrics. Certainly not Hafield or National Health in terms of song length or construction, but definitely there was a continuing line. Since then, it's been a long wait until their new, hot off the press, "Green and blue" disk which arrived last week.

Why did I start off by writing about the awkward point when an artist whose previous work I have loved creates something which leaves me cold? Because G+B leaves me extremely cold. Let's look at "Spin" again for a moment: it's possible to say that the songs on this disk fall into one of three categories: covers, witty uptempo songs, and slow dirges. Generally speaking, I liked the covers (especially "Eight miles high"), loved the uptempo songs (especially the song about the 60s) and tolerated the dirges. This might have something to do with Barbara's voice, which tends to be non-descript. Anyway, G+B is composed almost entirely of slow dirges, and at the moment the best track on G+B is about as good as the worst track on "Spin".

I can't really name any names or give any examples because it all sounds near enough the same to me, and nothing has impressed. I will listen and listen again, but I have the feeling that this one is a dud - or to be more polite and probably more correct, my vision and their vision have parted company.