Refactoring for performance
A few weeks into actually using the MC2 I realized that the response time of the expression pedal was quite poor, running in the 100-200ms range. After doing some tests I worked out that it was because of the way messages were passed from the hardware thread that received the events, like analog to digital reads for the expression pedal, to the user interface thread, where they would be processed and sent back out the hardware thread to the Axefx. The problem with this was the the Raspberry PI is quite slow (or at least the version I'm using) and the user interface update speed was lagging. If the user interface is being updated, it's not processing messages. Hence the delay. But fixing it wasn't straight forward, because all the user interface code and Axefx code were all jumbled up like this:

So I took all the code that relates to the Axefx, and all the code that updates the user interface and separated them out into their own source code files. I called the Axefx specific code the "logic". Now I had 3 separate bits of code: Now I originally had 2 threads. And I decided that unless I really had to I would stay with those 2 threads, but the logic had to move from the UI thread, into the hardware thread. This meant that responding to MIDI and hardware events was decoupled from the user interface updating. Like this:

So hardware events, like the expression pedal value changing, would by processed immediately in the same thread and an outgoing MIDI message could be sent almost immediately, even if the UI was busy doing something. The latency was hugely improved. It's got to the point that it no longer has an impact musically. It seems to respond instantly.

In doing this I realized that I could hide all the details of each block of code behind a message passing interface. Which is excellent for thread safety. There was a little shared data that I wrapped in a mutex. But all in all the design feels nice and clean.

In fact I loved the design so much I'm rewriting i.Ftp to use the same architecture. Oh yeah, and because it was kinda badly written and wouldn't port to modern UI systems that don't have thread safe UI APIs.

Tags: mc2
Negative ADS1115 readings
I've been using an ADS1115 to convert the analog voltage from the expression pedal input, which is just a potentiometer really, to a 16 bit value. On the weekend I had a gig and both at the rehearsal and the set the expression pedal would flip to 127 (the maximum) when in the minimum position. It was also behaving erratically, the full range of output values was compressed into a very short throw of physical movement. I didn't technically need it for the gig so I just unplugged it.

So today I thought I'd get to the bottom of it. After switching on the logging and reproducing the issue it was apparent that a lot of "plugged" / "unplugged" events were happening. This was due to the values coming from the ADS1115 were bouncing between very low values (say 0 - 100) to very high values (65530 - 65535). I started by putting the multi-meter in voltage mode over the input pin of the ADS1115 and ground and found that the lowest voltage was about 0.1V which is, well, correct for the minimum position. So that led me to believe it was solely a software issue. Looking at the code that does the read, I found it was assuming the 16bit value was a unsigned integer. But that fact that the value was jumping to 65500 made me wonder if maybe the ADS1115 was giving me a signed value instead. So I changed the code to assume signed behaviour and now the values all seemed to follow each other as the pedal moves back and forth. Then checking with the data sheet:
The ADS1113/4/5 provide 16 bits of data in binary twos complement format
Ugh, ok so I should read that more carefully. Still it going slightly negative like that even with a positive voltage is a bit weird to me. Oh well, I'm just going with max(0, AdcReadValue) for the moment and it seems to be working just fine now.

Tags: mc2
Scribe: Installer script hooks
So you know those red bars that appear in Scribe when it wants to install something? I've just added the spell check dictionaries to that so you have to confirm there install. This allows you to make sure you have a working internet connection before download a dictionary. But I thought it would be cool to add some scripting hooks for the install bar and the actual install itself. So take an example script like this:
function BeforeInstall(App, Msg, Actions)
    Msg = Msg + " (BeforeInstall was here)";
    return true;

function Install(App, Action)
    if (Action == "Scare")
        MsgBox(App, "Boo!");
        return false;
    return true;

function Main(App)
    if (!AddCallback("OnBeforeInstallBar", "BeforeInstall"))
        MsgBox(App, "Couldn't add BeforeInstall");
    if (!AddCallback("OnInstallComponent", "Install"))
        MsgBox(App, "Couldn't add Install");

    return 1;
What it does is install 2 callbacks, one for the missing capability bar, and one for the install itself. It messes with the message and buttons available to the install bar. Then adds a new "action" that is caught later in the "Install" function to put up a dialog box. Might be useful for managed installs of Scribe.

Tags: scribe scripting
The week of Mac bugs
This week has been mostly about making the Mac build of Scribe more polished. It's always lagged behind the Windows build. And now it's catching up fast:

1) HIViewSetFrame inside kEventControlDraw is bad

For many years I couldn't work out why some calls to HIViewSetFrame wouldn't work inside some application I was writing. It turns out that called that inside the kEventControlDraw callback is actually bad. It leaves lots of the controls half painted of showing the previous contents of the screen. And no amount of calling HIViewSetNeedsDisplay is going fix it. The compositor thinks the old position of the control is up to date and won't repaint it.

Ultimately if you find you need to move a control during the re-paint you have to send a message to yourself and do it after that particular repaint has finished.

2) Apple Event handling

After 4 years I finally found the reason why Lgi apps wouldn't respond to AppleEvents that were passed to them on startup. I answered my own stack overflow question today.

3) Editing the Application Menu

The Scribe Application menu, that sits between the Apple menu and File menu has never had the normal "About" and "Preferences" items. And while there is a lot of discussion on how to do that for Cocoa apps, there is little to no information for Carbon apps. And yes, Scribe is still a Carbon app, although it does use a bunch of modern APIs for text and so on. I tried mixing some Cocoa calls to get at the right menu, but that didn't work. Then after hours of searching the internet I finally stumbled on to a mailing list post that describes how to do it. So it seems it's possible, just really obscure.

Tags: mac carbon
Drawing windows BUTTONs on coloured backgrounds
The default windows BUTTON drawing behaviour draws a 1px grey border around buttons. If you place the button on a parent window that is a different colour you get an ugly border like this:

So I decided to see what I could do about that. It turns out that there is no "setting" that can fix that. You either draw the whole control yourself or let the system draw it. Now I want the system look without the wrong coloured border. So I reimplemented the WM_PAINT like so:
case WM_PAINT:
    if (!GetCss())

    // This is the effective background of the parent window:
    GCss::ColorDef bk = GetCss()->NoPaintColor();

    // If it's not the default...
    if (!bk.IsValid())

    // Then create a screen device context for painting
    GScreenDC dc(this);
    // Get the control to draw itself into a bitmap
    GRect c = GetPos();
    // Create a HBITMAP in the same size as the control 
    // and the same bit depth as the screen
    GMemDC m(c.X(), c.Y(), GdcD->GetColourSpace());
    // Create a HDC for the bitmap
    HDC hdc = m.StartDC();					
    // Ask the control to draw itself into the memory bitmap
    // End the HDC

    // Draw correct background
    m.Colour(bk.Rgb32, 32);
    // The outside 1px border (unfilled rect)
    m.Box(0, 0, c.X()-1, c.Y()-1);
    // The 4 pixels at the corners
    m.Set(1, 1);
    m.Set(c.X()-2, 1);
    m.Set(1, c.Y()-2);
    m.Set(c.X()-2, c.Y()-2);

    // Now stick it on the screen
    dc.Blt(0, 0, &m);

    // Skip over calling the parent class' callback procedure.
    return true;
What it does is get the system to draw the button into a memory bitmap and then "fix" the border, before blting the whole thing to the screen. Now it's implemented with LGI types and classes, but the whole idea is fairly straight forward that you could implement in raw Win32 API calls. This fixes the border and still looks native:

The key is re-purposing the WM_PRINT message to get a copy of the control's native look. For the moment I've limited this to just when running on Windows 7, because I think Windows 8 and 10 use flat colour controls and [possibly] don't need fixing like this.

Tags: windows
Logic Pro X bounce time
I was doing some mixing today and the bounce time in Logic Pro X seemed very long. Much slower than I was expecting. (Bouncing is basically "rendering" all the audio down to a stereo track for those wondering).

I opened up the Activity Monitor and Logic was reading 10mb/s off a disk capable of 100mb/s. CPU was about 30%, i.e. it's using one core (of my 4 core i5 machine). In true programmer fashion I started to google the problem and it appears that people are having trouble with various plugins being slow. I didn't have anything much loaded, just lots of audio tracks. But I tried disabling the reverb to see if it made any difference. None whatsoever.

I began wondering about the disk speed. Maybe it's causing problems? So I copied the project (all 50GiB) onto a fast USB3 flash drive and run my test again. The export of 2 songs went from 2 minutes to 20 seconds. Uh, ok, wow that's pretty different.

Then I started to wonder about why that is so. The USB3 flash drive is capable of 190MiB / second, and the original hard drive can do 110MiB / second. It's in the same ball park really... so why the order of magnitude performance difference? I think it comes down to the read strategy of Logic. Now I'm just guessing here, but I think it reads small blocks (KiB) of audio from the files to keep the in memory buffer of audio small. This means in large projects with many audio tracks seeking from one file to the next constantly trying to get the next little bit of the audio. This is kinda worst case for a mechanical drive that has to move the head to a new location between each file. Seeking is expensive. On the USB key, seeking is almost free... so it runs fast.

Now the authors of Logic could have bumped the buffer size up considerably so that when trying to access lots of audio tracks there would be more reading and less seeking between files. In my case with 16GiB of RAM to burn that would be fine, but might be an issue with smaller RAM sizes. You certainly can't afford any paging during audio work. Still Logic should be able to see how much RAM is available and size it's internal buffers accordingly.

Obviously for anything to do with "live" audio the buffer size has to be tiny, so that the latency is short. But for pre-recorded material buffering up a few MiB's of audio shouldn't be an issue.

Tags: logic audio
MC2 April Progress
So here is a scary idea... I'm playing next weekend in my live band. Should I use the MC2 for the first time? If I get a bunch of stuff finished I don't see why not, although I'll definitely have the MC1 packed in case it doesn't go smoothly. The main hurdle will be the expression pedal ports which I haven't really done any work towards other than getting the ADS1115 chip to show up on the I2C bus. Nothing like a dead line to get you moving.

Running progress:

Tags: mc2
MC2 March Progress
After when seems like forever I bring you an update on the MC2 project. The good news is that most of the hardware is now working, including MIDI Input/Output, the Neopixel chain, the I2C bus to the GPIO expander chips, which in turn connects to the buttons and encoders. In short I can now use it to change presets on the Axefx which is like the bare minimum functionality.

Some of the issues I've had to sort out: Oh and here is some video showing it working:

Tags: mc2
Got the NeoPixels working

I've got around to working on the MC2 again. Last I got up to was getting the NeoPixels working and for some reason none of them would light up. To isolate the issue I used the bread board to step the data signal from 3.3v up to 5v. As you can see it works great. Now I know the NeoPixels themselves, and their connections are good I can get to work finding the issue on the peripheral board.

Tags: mc2
Core Text LOL

Sometimes you just have to laugh.

I let XCode 7 install. And now I get the choice of the 10.11 SDK or the 10.11 SDK. Apple have finally removed the ATSUI API I was using to draw text in my Mac ports, causing compilation errors. So time to pull out the partially complete CoreText implementation and get that working.

Not quite there yet ;)

Tags: scribe mac


05/02/2016 1:13pm

Merry Christmas
I think for the first time ever? There is a simultaneous release of Scribe on 3 different platforms: Windows, Mac AND Linux. The new v2.1.3 build is tri-platform. The Linux (x64) build is a GTK2 app and runs ok on Ubuntu and Arch. Well that's all I've tested at this point.

It also runs on a Raspberry Pi 2 although it's a little slow. I know because I got bored and tried building it while working on some MC2 stuff.

Links to the i.Scribe builds:

Tags: scribe linux
Portable OpenSSL for Linux
A year ago I worked out how to make a portable build of OpenSSL for Mac. And now with the imminent release of the Linux build of Scribe I need to do the same with the Linux build of OpenSSL.

After downloading and unpacking OpenSSL I configured it with:
./config shared -DPURIFY
The reason for adding the -DPURIFY #define is to suppress valgrind errors related to uninitialized memory usage.

Once that is built I had a look at the shared object paths with ldd:
matthew@ubuntu:~/Downloads/openssl$ ldd ./ =>  (0x00007ffc0e197000) => /lib/x86_64-linux-gnu/ (0x00007f5c5ab6c000) => /lib/x86_64-linux-gnu/ (0x00007f5c5a7a7000) => /lib/x86_64-linux-gnu/ (0x00007f5c5a5a3000)
	/lib64/ (0x00007f5c5b1c1000)
So it's not portable yet. I initially tried using the chrpath command but it can only modify existing RPATH records. My binary doesn't have any RPATH yet. So that didn't work. The next thing I tried was the patchelf command from here:
After building and installing I issued this command in the OpenSSL folder:
patchelf --set-rpath '$ORIGIN' ./
Now to check if libssl pulls in the local libcrypto:
matthew@ubuntu:~/Downloads/openssl$ ldd ./ =>  (0x00007ffcdce86000) => /home/matthew/Code/Scribe/trunk/./ (0x00007f39b99d8000) => /lib/x86_64-linux-gnu/ (0x00007f39b9613000) => /lib/x86_64-linux-gnu/ (0x00007f39b940f000)
	/lib64/ (0x00007f39ba0a5000)
Yes! Now this build can be installed in the same folder as the Scribe binary and Scribe will use it without interfering with the system OpenSSL which is often out of date, or not built with -DPURIFY or missing.

Tags: shared-object linux openssl scribe