2020-10-20

Audio goodness

I have a gamebox1 and like to play games where the sounds carries information. So I need stereo, and I need enough volume to hear what is going on in the game soundscape.

But ...

  • These games can be pretty noisy and other people in the house don't want or need to be disturbed by my entertainment
  • The household also includes a toddler and a couple of people with moderate to severe health issues. I need to be able to hear when I have to stop playing right now and go take care of something important.

So getting a good pair of over the ear headphones and shutting the doors (the go-to solutions for hearing your game) are not really good ideas.

Now, a couple of years ago I spotted a solution: a wearable speaker. But at the time my finances were rather tight and before they recoverered I had forgotten. Then one popped up on woot: a JBL Soundgear.

The experience of using this thing for gaming is immersive. The sound is rich and seems to emenate naturally from all around you. It's hard to believe that everyone around you isn't hearing the same thing, but if you hand the device to a friend to try out you hear essentially nothing while they're wearing it. Of course you still hear local environmental noises which isn't ideal if you are listening for a telltale footfall in-game, but I chose this route because I need to be connected to my real-world environment.

These things might not be for everyone, but I'm pleased.


1 My gamebox is a PlayStation 4. Alas, Sony cheaped out on the bluetooth audio support (in an effort either to save a few cents per unit or to squeeze a few more bucks out of the consumer by controling the supply of compatible devices. You'd find it very easy to convince me of the latter. So I also bought a Avantree DG80 USB to bluetooth audio device. Works like a charm.

2020-10-19

Why is it always "percentage"?

I got a side assignment at work Friday: write a test harness and tests for a pieces of software a colleague is writing.

This is suppose to be a reference implementation, so having different people write the code and the tests is a good idea: it will tend to expose ambiguities in the specification and unstated assumptions in the implementations.

"No problem," I said, "but I'll need a copy of the spec."

As I'm reading the spec today I come to:

The input value, given as a precentage in decimal format (a value between zero and one)

Sigh.

Now, there is a strict reading in which this is technically correct, but that reading conflicts with the lay uage of "percentage" in a way that leaves room for ambiguity. The parenthetical clause is necessary to resolve the ambiguity. In the tradition of academic physics we'd have written "The input value, given as a fraction" explicitly because there is no chance that it will be misinterpreted.

2020-10-14

Well, what format are we using now?

Code formatting guidelines are good. Code formatting guidelines that people follow are better.

Now, my team is pretty conciencious, and we all try to follow the guidelines, but there are two issues:

  1. We don't actually have a written standard.
  2. We're all humans. Reasonably attentive but rather fallible humans.

One solution to this is to adopt some kind of formatting tool and require that it be used regularly (or better yet, automate its use). Great. That's the plan. Only...

  1. Now we actually have to chose a standard.
  2. We already have roughly 120k SLOC written by the aforementioned fallible, moderately attentive humans and we want to minimize churn on the repository caused by simply adopting this tool.1

The key here is to select a standard that reflects what we've actually been doing with our slightly different personal takes on things, our mixed level of attention, and our occassional outright errors. But how do we look at nearly four hundred files and distill what we've actually been doing?

Right. We get a program to do it for us.

Better still someone else has already written it: whatstyle. This thing attempts to write an optimal style file for you by trial-and-error optimization with a metric like "fewest changes". Though exactly what that means is somewhat debatable.

It supports several common beautifiers including clang-format which is what my colleagues are using. It gave what looked like quite reasonable results when I ran it on a single large source file from my repository, so now I'm running over a large fraction of my repository at once (excluding a directory of legacy code we borrowed from another project and some "include this in your project" third-pary code).2 The only issue is that it took roughly a minute to do the one file (it is examining hundred to tens of thousands of style definitions in each optimization round), and now I've handed it more than three hundred files. For the moment my work laptop is functioning as a space heater.

My plan is to send the suggested format file to every coder involved in the project, ask for suggestions, and try to mediate the resulting chaos.

Wish me luck.


1 You're right. An experienced project leader would have adopted the fool thing at the beginning. I'll keep that in mind next time around.

2 Something roughly like this:

find ~/Projects/Foo -type f \
             -and \( -name '*.h' -or -name '*.cpp' \) \
             -and -not \( -name 'thirdpartycode.h' -or -path '*LegacyLibrary*' \) \
             -print0 | xargs -0 \
             whatstyle -f clang-format 

Then wait.

2020-10-11

Getting what you want

[It is an] established fact that, despite everything society can do, girls of seven are magnetically attracted to the colour pink.
Terry Prattchet

We use plastic tableware. Not because of the toddler, though we use her as an excuse, but because the toddler's honorary grandmother has a neurodegenerative condition that cuases her to drop things from time to time.

Now, plastic stuff has to be replaced fairly oftern, and the latest round of soup/salad/full-meal-potion bowls came in several colors including light pink. The toddler just adores the pink.

Tonight I told her that she had to choose a smaller bowl to put her cookies in, but the only colors of smaller bowls avaliable were not acceptable. I would not be moved and left her complaining on the kitchen floor.

But shortly after I left I the schreeching abruptly stopped and I heard the sound of a chair being pushed across the floor. It was not long before the toddler appeard with the pink bowl with a couple fo cookies lurking suspiciously in its depths. Score one for the toddler. It was too much effort effort to get back up and tell her I cknew what she was up to.

2020-10-03

"Safe" string building in c

The deficiencies of the c standard library where strings are concerned have been discussed at length in many places, but I'd like to talk about one issue in particular: building up strings from pieces.

This week I wanted to build up some strings from pieces. Lots of little pieces. In a language with "real" strings this would be easy, you'd just do something like string result = substring1 + substring2 + substring3;, but c does not support that in any general way.1 There are really only two classes of tools available: strcat/strcpy functions from string.h and sprintf functions from stdio.h. Essentially all the "plain" functions will buffer overrun, and the "n" variants only protect you if you pass the right length (where "right" means paying attention to which functions count the terminating '\0' and which don't). Let's take a closer look at some of them .

strcat, strncat (and strcpy, strncpy)

On the face of it this is the "obvious" thing to do. After all it concatenates strings, right? The main issue is that the first string needs to occupy a sufficiently large buffer or you run into trouble. Which means knowing the desired length when you create the buffer.

If you have a separate buffer you use a "copy" function to move the first string into it and then concatenate onto the end.

As a side note, if you are going to repeatedly apply strcat, do capture the return pointers from each call to avoid a Shlemiel the Painter algorythm.

sprintf, snprintf

Same basic problem: the target buffer needs to be big enough. On the up-side, you don't need extra elements for any little connector text you want to stick in between the substrings that you are recieving from the caller, the environment, the database, or whatever.

And that is really it.

Variable length arrays or malloc

So, you need to wait until you have all the parts, find the desired length, and then create a buffer. Fine. You have two choices: a dynamic allocation or variable length arrays.

The issue with variable length arrays is that they weren't stanadrdized until 1999 and then were made optional in 2011 (and at least one major compile does not support them). Code that depends on VLAs is going to have limited poratability.

So you're going to have to put it on the heap with all the hassle and risks that entails. Great. Be sure to check the return value.

Recipe

All this is old hat and there is a well known hack to accomplish it. You use the "returns the length that would have been written" feature of snprintf functions like this:

nonnullcount = snprintf(NULL,0,...);
buf = malloc(nonnullcount+1); /* watch out for the with/without '\0' issue! */
/* check for errors */
snprintf(buf,nonnullcount+1,...); /* watch out for the with/without '\0' issue! */

It's not hugely time efficient; the two passes through the formatting engine are inellegant, but it's very flexible and does the job.

The three lines of code I exhibited there are idomatic and easy to recognise, so sprinkling them through your code wouldn't be too bad. Except that the error checking is pretty improtant and it will add to the length and break up the visual block. Not nice. So I'd like to encapsulate all that in a function. Which is where it gets technical; how are you with c's variadac function support?

Wrapped up and made pretty I get something like (written to be compatible with c89 compilers even if they would require a more up-to-date libc):

/* A "safe" auto-allocating sprintf.
 *
 * Returns a pointer to a malloc'ed buffer containing the resulting string or
 * NULL if an error occurs. Leaves errno set following the error.
 *
 * It is the caller's responsibility to free the buffer.
 */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>

char * smallocprintf(const char * format, ...)
{
    char * buf = NULL;
    size_t nonnullcount = 0;
    va_list args;
    va_start(args,format);

    nonnullcount = vsnprintf(NULL,0,format,args);
    va_end(args);
    buf = malloc(nonnullcount+1); /* +1 for the '\0' */

    if (buf == NULL)
        return NULL; /* Leave errno intact for the caller to deal with */

    va_start(args,format);
    vsnprintf(buf,nonnullcount+1,format,args); /* +1 for the '\0' */
    va_end(args);

    return buf;
}

Frankly I imagine that code like this exists in private libraries all over the place, but I hadn't seen it myself, and it does what I need. Late edition: In particular this seems to be a close analog to the GNU c library function asprintf though there are some interface differences.


1 String litterals may be simply concatenated in code, but not null terminated character arrays and buffers.