High Speed FTDI + Android Comms (OR) Why Am I Always Reading 0 Bytes?

I’ve been working on a project with some very unfamiliar tech. The project involves communication between a new Android app (Kotlin) and an FTDI 232R connected to an Arduino. I encountered a problem that baffled everyone on the team for weeks and was about to be labeled a “general incompatibility between FTDI devices and Android apps” until I stumbled upon the solution. Before I describe the solution, I’m going to document some basic details. While working on this, I was unable to find any examples of people having this problem, so there was an intense feeling of isolation as I struggled on and off for weeks to resolve it. My hope is that this might help someone identify the same problem in their system.

tl;dr

If you’re experiencing mystery “0 bytes available” errors, you might need to change your latency timer setting. The problem is described here. I also strongly recommend you read the longer document from which that excerpt is drawn, AN23B-04 Data Throughput, Latency and Handshaking. We immediately resolved our issue with a call to setLatencyTimer((byte) 1); and very small reads (64 bytes at a time, no more) but ultimately settled on an event character and larger reads. Full details below.

Detailed Notes

Our Arduino’s firmware is capable of sending a few different messages across the wire. Each message is small, anywhere from 16 bytes up to around 256. Most of these are on-demand: send a command from the application, the Arduino decodes it, then it sends one message in response that is either an ACK or the data that you’ve requested. There is one exception: one particular message from the app will trigger the start of an infinite stream of 44-byte messages at a frequency rate specified in the request. In this case, the Arduino is reading sensors, performing some basic analysis, and spitting it out across the wire for the app to do with as it pleases. The app reads this constant stream of bytes, does its own analysis, puts it on the screen, etc,…

Our minimum acceptable streaming rate is 300hz but we hope for closer to 500hz or greater, so our baud is currently 460800.

We encountered an issue whereby the app was constantly being told 0 bytes were available for read. The problem was extremely inconsistent and weird. The following were true:

  • We could ALWAYS open the port from our app.
  • We could ALWAYS transmit successfully from the app to the Arduino. We knew this because logs on the firmware indicated that the right bytes arrived in the right order.
  • We would SOMETIMES receive the correct response. It was all or nothing: sometimes we would query for available bytes and be told 0, other times we would see the expected number.
  • We could RARELY start the data stream. Once we sent the message to start streaming, the app would always believe there were 0 bytes available for read. Once that state was encountered, no other messages would be sent across the wire until we rebooted the firmware. It seemed to be more likely to fail as our streaming rate exceeded 100hz. Our target was 300hz or greater, so this was a serious problem!

Adding to the mystery, this seemed specific to the FTDI chip. Our first draft of this used the Arduino’s programming port for serial data transfer at 115200 baud. We were losing a lot of packets from the lack of flow control but it never failed to respond to messages.

More troubling was the fact that a C++ test application seemed to communicate correctly. This pointed towards a code problem with the Android app.

We tried three different libraries in attempts to resolve this. Those were:

  • usb-serial-for-android - An open-source library that is pretty well maintained and offers a lot of features. Unfortunately, it doesn’t support automatic flow control, so we worried we wouldn’t be able to use it long term.
  • UsbSerial - Another open-source library. This one is not nearly as well maintained and it has quite a few open issues that describe some pretty heinous bugs. I opened an issue after I found that calling the wrong method during initialization would result in all your sent messages being replaced by two 0 bytes for every one byte in your message! Brutal. It supports flow control but it has so many problems that I unfortunately couldn’t recommend it, even if it supported what we needed.
  • FTDI’s official d2xx - The official closed-source library for FTDI devices. It hasn’t been updated in two years but by virtue of being official, we expected it to be more reliable or at least more full-featured. The closed-source part is a bummer and I think it would be a much better library if not for that, but that’s another story. This was the library we wound up using and we will continue to do so.

All three of these libraries exhibited the same behavior! This started looking like a major issue with FTDI devices. I ordered a few Prolific PL2303-based serial cables to test as an alternative but kept researching in the meantime.

I began looking at FTDI’s official test apps and their example Android app code. The example code is… not… great… but in taking notes, I came across a mysterious call to setLatencyTimer(). This led me to this, which appeared to describe our problem exactly. It specifically remarks, “While the host controller is waiting for one of the above conditions to occur, NO data is received by our driver and hence the user’s application. The data, if there is any, is only finally transferred after one of the above conditions has occurred.” I did some more reading and found the longer AN23B-04 Data Throughput, Latency and Handshaking which explained this and many other concepts. This document was particularly enlightening. I feel like the embedded software development world is full of extremely dense, unapproachable technical specs that assume a ton of highly specific knowledge; by comparison, this document was a breath of fresh air and explained things from a high enough level that I came away feeling more capable of anticipating behavior as I continued troubleshooting.

It appeared that we were never hitting any of the three rules fast enough to trigger a read. It still doesn’t totally make sense to me; I feel like we should have eventually hit 4Kb to trigger the send, but maybe I never let it sit long enough to get there? Or maybe there was another timeout value that was clearing the buffer before then. What I do know is that if I set the latency timer down to 1ms and ensured we never requested more than 64 bytes at a time, our data read problems went away. We could stream at 500hz and messages would usually start showing up as soon as we hit the button. This change was as simple as setLatencyTimer((byte) 1); and making sure that we never requested more than 64 bytes during a call to read. The immediate problem was solved and it was clear that we did not have some incompatibility between FTDI and our Android app.

I say that it would “usually” start showing up because it still exhibited strange behavior. Very often, I would start the stream through the app’s interface and nothing would happen. Then I’d send another message (“get hardware version”) and not only would it get my hardware version, it would also recognize that data was streaming in. Other times, I would request our largest payload, a system configuration, and it would return 31 bytes of the 200+ we expect. Just like with the stream, I’d send any other message (“get firmware version”) and it the remaining 200+ bytes would show up.

I wound up making a few other changes to resolve this problem and improve the behavior overall.

First, using more information gleaned from the Data Throughput, Latency and Handshaking document, I thought that we be better off using the FTDI’s support for Event Characters than the latency timer. Our encoding rules use a 0 byte as a delimiter, so it was an obvious choice. This allowed me to increase our maximum read size up to 256 bytes, which helped in the event that our read loop was delayed and we had to quickly get through a backlog of data. (I could probably go higher but I’m being pretty careful right now, I want to keep things moving.) Finally, I modified the read loop to also be responsible for writes, added a FIFO queue for outgoing messages, and (crucially) a 50ms timeout of the loop after every single message sent. The 50ms timeout was the most significant piece – it was the final change that ensured that we stopped seeing partial messages or messages that only arrived after a subsequent send. I don’t have a good answer for why that was necessary but given the complexities of the d2xx library, reading from USB in general, the FTDI and its buffers, and the Arduino, it’s not too surprising that things can get out of sync if you’re moving fast.

With the implementation of the event character, the buffered writes added to the loop, and the timeout after writing, we appear to be running smoothly. So smoothly, in fact, that I was able to remove the setLatencyTimer call entirely and just leave it at its default. As configured, data is sent as soon as a 0 is hit or 256 bytes are available, whichever comes first. (Typing this out, I realize that I should probably just set it to the exact size of our largest message, there’s no way it could ever be smaller and having an incomplete message does us no good!)

To summarize, we went through two rounds of improvements that changed our situation from bleak to beautiful.

Round 1:

  • Set the FTDI’s latency timer to 1ms
  • Limit our max read size to something small to prevent a “jerky” feel

Round 2:

  • Revert latency timer to default
  • Enable an event character keyed to our delimiter, a 0 byte – this is the key
  • Set a max read that’s a bit bigger than our typical messages to help us catch up if we ever have a huge backlog and want to get the queue down (again, I don’t know what this situation is)

As it happens, the d2xx library is the only one of the three that supports configuration of latency timer, event character, and flow control. One of the two open source libraries supports the latency timer, the other claims to support flow control, and neither supports the event character. Only the closed-source official FTDI library d2xx supports all three, so we’ll be sticking with that.

It appears that our use case of extremely high streaming rate combined with tiny messages at a very high baud is somewhat unique. If we had been sending larger messages at a slower rate, I don’t think we would have encountered this. Our 44 byte messages at 300hz were the problem.

I spent many lonely weeks fighting with this. Failure to resolve it would have been a major problem for the project. In the end, the solutions I found were new to the whole team, which included many people with much more experience than me when it came to FTDI chips, which should go to show you how esoteric some of these configuration parameters very well may be. This is my first project writing Kotlin, working on Android, or using FTDI devices at all, so I while I’m disappointed that it was such an unpleasant struggle to get it done, I am pleased to have it behind me. I sincerely hope this helps somebody avoid going through the same experience.

Three.js + TypeScript + MTL + OBJ + MTL + WTF

At work, we’re working on implementing a very cool 3D visualization for use throughout our app. I’m not prepared to demo it but I am very prepared to talk about its architecture, cause lemme tell you, it was quite a challenge. It took a significant amount of help from a friend who is a 3D artist, many three.js code snippets, careful reading of documentation and source code, some Stack Overflow, and some good luck before I was able to consider this a success. I hope that the details I provide will help someone else save some time.

First, some background. I maintain a React app built using TypeScript. I use three.js in a few places in our app, but to date, this has always been somewhat simple: we take positions in space, we draw some spheres, we add a mesh, and… that’s it, really. In one view, we stream position data in from our hardware and I move a sphere accordingly; in another, we just present captured data and it’s relatively static. This was challenging when I was completely new to three.js but now that I’ve spent some time with it, I now feel like it’s pretty straightforward.

Enter the retail 3D model. We obtained resources in Maya format and needed to get it into the browser. Additionally, it needed to render quickly, since we target a somewhat under-powered device and often have multiple instances of the visualization in a given window, and it needed to be somewhat small, since some of our users are in isolated areas where they must rely on satelite internet. With every step, I encountered new challenges. It was like a boss rush in a video game. Below, I will walk through some of the key portions of this process and provide you with a few different ways of completing this task.

Before continuing, I’ll say that this is not going to be an exhaustive dive into how everything works. I’ll provide some detail but it will help for you to have a solid understanding of creating a basic three.js scene. You know about objects and the scene, the camera, lights, the renderer, meshes, geometries, objects, etc,… You also understand the basics of OOP, since three.js relies heavily on this, and you understand why we require and import content. You’re using webpack, casuse you’re gonna need it for some of this. You’re probably using TypeScript but you can easily ignore some of that if you need to and it’ll still work, it’ll just require you to keep more in your head.

Finally, I want to say that I’m not an expert on this and it is likely that better ways of solving this problem exist. Please don’t be mad at me if something here sucks. All code samples were adapted from my production code, so if something seems wrong or broken, it is probably due to my hasty copy/paste/refactor to omit pieces that won’t matter to you.

Ready? Let’s do it.

First attempt: Rigged model to OBJ and MTL

Maya, Blender, and I’m going to any other 3D modeling software worth using will support exporting to the OBJ and MTL formats, so we start with that. These are big text files and you can open them in your preferred text editor if you’d like. In my case, it was necessary to modify the MTL so it could find my materials’ images, which existed as .jpg files. Those modifications go beyond the scope of this post but please email if you need help, I can give you some tips.

Once exported, Three.js provides two classes, OBJLoader and MTLLoader, that make this somewhat simple, but there’s still some work to be done.

When dealing with all this stuff, I found that I was constantly trying to satisfy the needs of three different parties: the three.js library, the code as it relates to Webpack, and TypeScript. This was a recurring challenge that got easier as I went.

The completed code for my first attempt looked something like this:

// satisfy TypeScript
import {
  MTLLoader,
  OBJLoader,
  Scene
} from 'three';

const OBJ_NAME = 'viz.obj';
const MTL_NAME = 'viz.mtl';

// make webpack aware of the content so I can reference it in the code
[OBJ_NAME, MTL_NAME].forEach(filename => {
  require(`../../../public/3d_resources/${filename}`);
});

// satisfy the three.js libraries
require('imports-loader?THREE=three!three/examples/js/loaders/MTLLoader.js');
require('imports-loader?THREE=three!three/examples/js/loaders/OBJLoader.js');

// in your `constructor` or in some instance method called

class VizWrapper {
  scene: Scene;

  constructor() {
    // renderer, camera, scene are all created -- code omitted
    // finally...

    new MTLLoader().load(`/static/media/${MTL_NAME}`, creator => {
      creator.baseUrl = ('/static/media/');
      creator.preload();

      const objLoader = new OBJLoader();
      objLoader.setPath('/static/media/');
      objLoader.setMaterials(creator);
      objLoader.load(OBJ_NAME, obj => {
        this.scene.add(obj);

        // then start the render loop using your custom `animate` method
        this.animate();
      });
    });
  }
}


A few things of note here:

  • We import at the beginning to keep TypeScript happy. Strangely, this will bring the types into scope but it doesn’t actually load the libraries, I guess because something something three.js?
  • We already modified our webpack config so file-loader knows what to do with obj and mtl files. This ensures that explicit require statements will put them in /static/media, which we need for our code.
  • Three.js libraries have the obnoxious quality of expecting a global THREE variable. We can use imports-loader as you see above to feed it THREE, which satisfies their dependencies and brings the contents of those libraries into scope so we can actually use the classes in our code.
  • I don’t hang onto my MTLLoader or OBJLoader, I let them get garbage collected.
  • The exported OBJ file will have names that correspond to materials in the MTL, and the MTL will have details about how to find files on disk.
  • My initial export from Maya gave me MTL files that didn’t work correctly in Three.js. I had to modify them, email me if you need help.

This will work but it’s SLOW. We’re doing this whole process every time the visualization loads. Maybe that’s ok for you, maybe you have a single view and it’s ok for it to take a moment, or maybe your files are small, but for me, it was a problem.

An additional issue I had was that my rendered model was hideous. There were no curved edges, which I learned is called a “faceted model.” Think Virtua Fighter if you’re in your 30s and grew up with that generation of arcade games. We’ll discuss this later, but tuck it away for now.

PROS of this method:

  • It’s simple to implement.
  • You can find plenty of examples of this out in the world.

CONS of this method:

  • It’s slow and it is a blocking operation. Your whole browser will hang.
  • OBJ files are BIG, possibly too big for you.
  • Imported files are ugly.

Those cons were totally unacceptable for me, so I kept going.

Second attempt: Rigged model to OBJ and MTL with OBJLoader2

In addition to OBJLoader, three.js provides a different loader, OBJLoader2. Its developer and maintainer works from a separate repo here, where he explains many aspects of his loader. In addition to supporting more of the OBJ spec, it also uses Web Workers to improve performance, the first of the three issues identified below. Unfortunately, I found fewer examples of how to make this work. My first draft of this looked something like this:

import {
  Group,
  MaterialCreator,
  OBJLoader2
} from 'three';

const OBJ_NAME = 'viz.obj';
const MTL_NAME = 'viz.mtl';

[OBJ_NAME, MTL_NAME].forEach(filename => {
  require(`../../../public/3d_resources/${filename}`);
});

// Note the additional dependency required by OBJLoader2
require('imports-loader?THREE=three!three/examples/js/loaders/LoaderSupport.js');
require('imports-loader?THREE=three!three/examples/js/loaders/MTLLoader.js');
require('imports-loader?THREE=three!three/examples/js/loaders/OBJLoader2.js');

class VizWrapper {
  constructor() {
    // once again, initialize scene, renderer, camera, etc
    // finally...

    const objLoader2 = new OBJLoader2();
    objLoader2.setPath('/static/media/');

    const onLoadObj = (event: any) => {
      const group = event.detail.loaderRootNode as Group;
      this.scene.add(group);

      // start the animation with your custom method
      this.animate();
    };

    const onLoadMtl = (loadedMaterials: MaterialCreator) => {
      objLoader2.setModelName('my-viz');
      objLoader2.setMaterials(loadedMaterials);
      objLoader2.load(OBJ_NAME, onLoadObj, null, null, null, false);
    };

    objLoader2.loadMtl(`/static/media/${MTL_NAME}`, null, onLoadMtl);
  }
}

Some notes:

  • OBJLoader2 is required instead of OBJLoader.
  • We need an additional dependency, LoaderSupport, before we require OBJLoader2
  • OBJLoader2 relies on a series of callback functions that need to be defined ahead of time. I find this harder to reason about, I don’t know why.
  • OBJLoader2 uses MTLLoader under the hood. Looking at this code, I’m wondering if I need to require MTLLoader.js at all? Experiment with that.
  • The onLoadObj callback gets an instance of Event, an interface not defined anywhere. Looking at it in the console, I noticed that event.detail.loaderRootNode is a Group, the object we’re used to dealing with. we can just target that and proceed normally.
  • The onLoadMtl callback function is calling methods on objLoader2, which we need to define that at the start. It will link up objects to their materials for you.
  • We kick this process off by calling objLoader2.loadMtl. The overall process is essentially the same: load the materials, then load the geometries, then add it to the scene, but we wrote the code backwards. Cool… :-

PROS:

  • This is MUCH faster when it comes to actually getting everything loaded. Loading is no longer a blocking operation.
  • It uses the same resources we already created for the slower OBJLoader procedure, so it’s basically a free performance update.

CONS:

  • There’s fewer documentation and resources available for this. If something goes wrong, it’s harder to find help.
  • I personally found this harder to reason about and write. It felt backwards.
  • There aren’t types available for some of these objects, so I’m defeating type safety with those explicit as SomeCrap expressions. Unfortunate.

Outstanding:

  • Still haven’t solved our file size issue.
  • Still have flat, ugly surfaces.

Third attempt: Smoothed OBJ and MTL with OBJLoader2

Our files were importing faster but it was ugly as hell. My 3D consultant friend remarked that the problem was possibly the file type – we should consider FBX instead of OBJ – or it wasn’t being smoothed correctly at some point. She theorized that we could either export differently or pursue smoothing in three.js. A lot of googling and reading later, I discovered that this is a pretty well-recognized problem with an easy fix: compute vertex normals in your geometries.

This is one of the posts detailing the process. It describes a problem, which is that all of the Loader classes use BufferGeometry, which does not allow for the changes we need to make. The solution is to convert your buffer geometries back to less efficient Geometry objects, clean them up, and then turn them back into BufferGeometry. This was correct, but it was missing one critical step that I’ll highlight below.

Here’s what it looks like:

// using the same objLoader2 example, only changing the contents of `onLoadObj`

const onLoadObj = (event: any) => {
  const newObj = new Object3D();
  const group = event.detail.loaderRootNode as Group;
  group.traverse(mesh => {
    if (mesh instanceof Mesh) {
      const newGeometry = new Geometry().fromBufferGeometry(mesh.geometry as BufferGeometry);
      newGeometry.mergeVertices();
      newGeometry.computeVertexNormals();

      // THIS IS THE MISSING PIECE!
      (mesh.material as Material).flatShading = false;
      newObj.add(new Mesh(new BufferGeometry().fromGeometry(newGeometry)), (mesh.material as Material).clone())
    }
  });
  this.scene.add(newObj);
  // start the animation with your custom method
  this.animate();
};

Instead of just taking the group and throwing it in the scene, we break it apart, mergeVertices, computeVertexNormals, disable flatShading on the material, and build a new Mesh. We add this mesh to a new Object3D and then add that to the scene. We end up with a smooth, curved 3D rendering! Victory!

PROS:

  • It’s curved and beautiful! Success!

CONS:

  • Our files are still huge.
  • This is even slower cause now we’re doing all this extra work on every load.

Fourth attempt: Rigged model to FBX

We’ve got a nice looking model but it’s slow and huge. My friend had mentioned the FBX file as an alternative to OBJ and I was curious about whether that could solve some of these problems. Three.js has an FBXLoader, so we decided to give that a shot to see what would happen.

We went back to the beginning and tried exporting our rigged model from Maya as FBX. Unfortunately, this threw some errors and the geometries were totally screwed up when importing into Three. After a little research and experimentation, I found an alternative: we could import our working OBJ into Blender and export back out to FBX. The export worked without an error and we could start experimenting in Three.js.

At this point, I discovered a great benefit of using FBX: its modern version is a binary format. Our 16.2 MB file was now a 3.7 MB file, completely reasonable for our use as long as it didn’t involve a huge drop in quality. Three.js didn’t support the binary format until recently, but we found evidence that it should work now.

After a significant amount of trial and error, I got this working. I’m going to demonstrate it first without materials and then I’ll explain how to do it with them.

import {
  FBXLoader,
  Scene
} from 'three';

(window as any).Zlib = require('zlibjs/bin/zlib.min.js').Zlib;
// tslint:disable-next-line:no-implicit-dependencies
require('imports-loader?THREE=three!three/examples/js/loaders/LoaderSupport.js');
// tslint:disable-next-line:no-implicit-dependencies
require('imports-loader?THREE=three!three/examples/js/loaders/FBXLoader.js');

const FBX_NAME = 'viz.fbx';
[FBX_NAME].forEach(filename => {
  require(`../../../public/3d_resources/${filename}`);
});

class VizWrapper {
  scene!: Scene;

  constructor() {
    const fbxLoader = new FBXLoader();
    fbxLoader.load(`/static/media/${FBX_NAME}`, group => {
      this.scene.add(group);
    });
  }
}

Notes:

  • We have an entirely new dependency: zlib, aka zlibjs on npm. Here. Not to be confused with zlib on npm, which will not work. This is required by the FBXLoader. We need to require it and attach it to the window, which is awful. There’s probably a way to use imports-loader to fix this.
  • We’re still using LoaderSupport, FBXLoader will die without it. We don’t need to import this or Zlib since we don’t interact with them in our code.

There’s a huge problem with this: FBX files usually have materials embedded, but I must have missed a step in my OBJ + MTL import into Blender, so the export lacked them. FBX is much more powerful file format, supporting animations, cameras, and lighting – things we don’t want in this case. There’s no clear way to separate them out and neither the Three.js documentation nor the code nor Blender provide any clear path to resolving it and explicitly providing external materials. This is unusable as is.

I played around with a few approaches to fixing it. I got my hands on a rigged Blender model, from which I could export an FBX with materials embedded, but this hurt file size and included those aforementioned objects that I want to avoid. I just want geometries and external materials! I went back to the drawing board.

PROS:

  • We’ve got a tiny geometries file.

CONS:

  • It doesn’t put anything on the screen because we don’t know how to connect it to our materials.

Fifth attempt: Rigged model to FBX with manual MTL hookup

Looking at the FBX source and type definitions, I noticed that the argument provided to the callback (second argument) of fbxLoader.load was an instance of our familiar Group. As we’ve seen, a three.js Group is a container of Mesh objects, and each Mesh object will have a Material. Curious about what my broken imported FBX meshes would recognize as their materials if I didn’t hook them up, I looped through and sent each to the console.

fbxLoader.load(`/static/media/${FBX_NAME}`, group => {
  group.traverse(mesh => {
    if (mesh instanceof Mesh) {
      console.log(mesh.material);
    }
  })
});

This revealed that each Material had a name key and these names were familiar: they were the same names referenced in our .OBJ and .MTL files. It seemed reasonable that there would be some way to manually do what objLoader and objLoader2 did automatically.

Being ASCII files, the .OBJ and .MTL files are pretty easy to make sense of if you look for commonalities, especially where materials are concerned. The .MTL file identifies each material with a newmtl directive, a string name; on the other side, the .OBJ has a very clear usemtl directive that relies on this same name. When using objLoader and objLoader2, we had a simple way of wiring these up: loader.setMaterials(creator). We do not have such luxury with our FBX Loader, where it expects the materials to be embedded in the file, but it does set that same name on the FBX’s imported-but-broken Material.

Examining the code, I noticed that the object provided to the mtlLoader.load was an instance of MaterialCreator. The three.js documentation for this object was severly lacking but the TypeScript definitions were not, and there we found one promising instance method:

create( materialName: string ) : Material;

It does exactly what you’d expect: give it a name that it knows and it will spit out a material. Here’s the complete code:

import {
  MTLLoader,
  Mesh,
  Material,
  Geometry,
  BufferGeometry,
  FBXLoader,
  Scene,
  Object3D
} from 'three';

(window as any).Zlib = require('zlibjs/bin/zlib.min.js').Zlib;
// tslint:disable-next-line:no-implicit-dependencies
require('imports-loader?THREE=three!three/examples/js/loaders/LoaderSupport.js');
// tslint:disable-next-line:no-implicit-dependencies
require('imports-loader?THREE=three!three/examples/js/loaders/FBXLoader.js');

const FBX_NAME = 'viz.fbx';
const MTL_NAME = 'viz.mtl';
[FBX_NAME, MTL_NAME].forEach(filename => {
  require(`../../../public/3d_resources/${filename}`);
});

class VizWrapper {
  scene!: Scene;

  constructor() {
    new MTLLoader().load(`/static/media/${MTL_NAME}`, creator => {
      creator.baseUrl = ('/static/media/');
      creato.preload();
      const fbxLoader = new FBXLoader();
      fbxLoader.load(`/static/media/${FBX_NAME}`, group => {
        const newObj = new Object3D();
        group.traverse(mesh => {
          if (mesh instanceof Mesh) {
            const newGeometry = new Geometry().fromBufferGeometry(mesh.geometry as BufferGeometry);
            newGeometry.mergeVertices();
            newGeometry.computeVertexNormals();
            newGeometry.name = mesh.name;
            (mesh.material as Material).flatShading = false;

            const finalBufferGeom = new BufferGeometry().fromGeometry(newGeometry);
            // We fix materials here
            const finalMaterial = creator.create((mesh.material as Material).name))
            const finalMesh = new Mesh(finalBufferGeom, finalMaterial);
            newObj.add(finalMesh);
          }
        });
        this.scene.add(newObj);
      });
    });
  }
}

That does it! We’ve created new meshes using the geometries from our tiny FBX file with the materials from our MTL file. I bet we could find a way to skip the MTL file and instead just load the JPGs directly from disk, but that’s a problem for another time. We’re almost there!

PROS:

  • Small files!
  • Readable code
  • Looks great

CONS:

  • Performance still isn’t great. Our load is a blocking operation again and we’re doing the same work on each init of our visualization.

Sixth attempt: FBX + MTL with reusable components

We’re almost there! It looks good and it’s small enough to send over the wire but we’re still blocking the DOM to import files and create Geometries over and over again.

There’s an easy way to fix this: geometries and materials can be saved and reused, but meshes cannot. Instead of doing all this work in our constructor, let’s do the heavy stuff once when the file is required and then do the bare minimum work when the visualization is loaded.

We’ll define a new TypeScript interface, VizPiece. We’ll also create somewhere to put our viz pieces so they won’t get garbage collected. This eats up a little memory but it’s better for performance. Then we’ll define some functions to load everything and kick it off right away. After that, we can just loop through our vizPieces array in the constructor of our wrapper classes, building Mesh instances along the way. This is pretty cheap and we’ll notice a significant performance improvement as a result.

It could look like this:

// viz_preload.ts
// requires shared files, interfaces, and calls upon your loader

import {
  BufferGeometry,
  Material
} from 'three';

const files = ['texture_jpg1', 'texture_jpg2'];
files.forEach(name => {
  try {
    require(`../../../public/public_resources/${name}.jpg`);
  } catch (e) {
    console.log(`could not require ${name}Dif.jpg`);
  }
});

export interface Viziece {
  geometry: BufferGeometry;
  material: Material;
  name: string;
}

// populated by the `load()` function below
const vizPieces: VizPiece[] = [];

import load from './fbx_loader';
load(vizPieces);

// for use in three.js code
export default vizPieces;
// fbx_loader.ts
// your loader is defined here, the load function is the only thing exported and it takes the array to populate

import { VizPiece } from './viz_preload';
import {
  BufferGeometry,
  FBXLoader,
  Geometry,
  LoadingManager,
  Material,
  MaterialCreator,
  Mesh,
  MTLLoader
} from 'three';

(window as any).Zlib = require('zlibjs/bin/zlib.min.js').Zlib;
// tslint:disable-next-line:no-implicit-dependencies
require('imports-loader?THREE=three!three/examples/js/loaders/LoaderSupport.js');
// tslint:disable-next-line:no-implicit-dependencies
require('imports-loader?THREE=three!three/examples/js/loaders/FBXLoader.js');
// tslint:disable-next-line:no-implicit-dependencies
require('imports-loader?THREE=three!three/examples/js/loaders/MTLLoader.js');

const FBX_NAME = 'viz.fbx';
const MTL_NAME = 'viz.mtl';
[FBX_NAME, MTL_NAME].forEach(filename => {
  require(`../../../3d_resources/${filename}`);
});

const loadingManager = new LoadingManager();

const loadGeometry = (loadedMaterials: MaterialCreator, vizPieces: VizPiece[]) => {
  const fbxLoader = new FBXLoader(loadingManager);
  fbxLoader.load(`/static/media/${FBX_NAME}`, group => {
    group.traverse(mesh => {
      if (mesh instanceof Mesh) {
        const newGeometry = new Geometry().fromBufferGeometry(mesh.geometry as BufferGeometry);
        newGeometry.mergeVertices();
        newGeometry.computeVertexNormals();
        newGeometry.name = mesh.name;
        (mesh.material as Material).flatShading = false;

        vizPieces.push({
          geometry: new BufferGeometry().fromGeometry(newGeometry),
          material: loadedMaterials.create((mesh.material as Material).name),
          name: mesh.name
        });
      }
    });
  });
};

const load = (vizPieces: VizPiece[]) => {
  new MTLLoader(loadingManager).load(`/static/media/${MTL_NAME}`, creator => {
    creator.baseUrl = ('/static/media/');
    creator.preload();
    loadGeometry(creator, vizPieces);
  });
};

export default load;
//VizWrapper.ts

import vizPieces from './viz_preload';

import {
  Mesh,
  Object3D,
  Scene,
} from 'three';

export class VizWrapper {
  scene: Scene;

  constructor() {
    // create your scene, renderer, etc...

    if (vizPieces.length > 0) {
      const newObj = new Object3D();
      vizPieces.forEach(vizPiece => {
        const newMesh = new Mesh(vizPiece.geometry, vizPiece.material);
        newMesh.name = vizPiece.name;
        newObj.add(newMesh);
      });
      this.scene.add(newObj);
      // your custom load method
      this.animate();
    } else {
      // handle this somehow
      // throw an error, load it manually, ignore it?
    }
  }
}

So to recap, our new process is like this:

  • We create an array to hold the component pieces of our visualization.
  • We populate that when the app loads, incuring a small performance penalty once at the beginning.
  • We loop through that array, combining those pieces to create usable objects, when the visualization needs to be displayed.

PROS:

  • On-demand renders of our visualization are fast and it looks good.
  • File size is small.

CONS:

  • We incur an up-front performance penalty for everyone, regardless of whether they are going to see the visualization or not.

That one might be a deal-breaker for you but maybe not. If it is, maybe you use the Web Worker-powered ObjLoader2. A pleasant option with this implementation is the flexibility to define multiple loaders, each exposing a load function that takes the same parameters, allowing us to swap everything out by changing the file from which we import. In my code, while I test, I defined one for each of the processes outlined above. It might also be reasonable to pursue an FbxLoader that uses Web Workers, but that’s something for another day.

So there you have it! This represents many days worth of troubleshooting, so I really hope it can be of use to someone else. If it is helpful, please shoot me an email and let me know, I’d love to hear what you think.

What Audio Engineering Could Learn from Software Development

Woe recorded two songs a few weeks ago. They’re planned for release later this year, medium TBD. Like the last album but unlike the two before that, it was not recorded and will be mixed elsewhere, which makes its chances of sounding good significantly higher than if I had been completely in charge and the likelihood of emerging with some sanity greater still. It’s cool.

Something that’s not cool is how primitive aspects of the modern digital recording process still feel. It’s not like I had any illusions about this – I still read about things and record my own demos, so I haven’t completely given up – but I became hyperaware of it as we started this mix process and talked about ways to collaborate on some additional tracks and work. It got me thinking about how so many collaboration and communication problems could be solved by tools and processes that are popular in the software development world. Here are a few.

PROBLEM: Version control of mixes

In my current workflow, I include the current date in a project’s filename and I never clean files unassociated with a project until I’m completely done editing. It’s a bit of a drag, since those filenames don’t include details about what changed or why, they’re just dates, which are sort of useful, but not really. It’s even worse when I’m writing over the course of days or weeks or even months, since I’ll invariably clean my project’s unassociated files, breaking old versions in the process. It’d be so great if I could keep track of a project and its audio dependencies as well as log distinct changes.

SOLUTION: Git LFS?

Git Large File Storage, or Git LFS, might do the trick. It’s like git, only for large files. Go figure. I have no idea how well it works, nor do I have an answer for how the audio world should handle things like merging branches, but for basic versioning, hanging onto audio dependencies, as well as having a places for notes about changes, this could be cool.

PROBLEM: Plugin Dependency Management

I’ve switched computers a few times over the years and I don’t always reinstall all the same software. This is especially true for my plugins, which I had a habit of buying greedily and forgetting after a mix or two. It’s rare that I need to open an old project, but without fail, if a few years have passed, I’ll get an error about missing plugins. It would be amazing if there was a single file that contained a list of all the plugins used in a project, the vendors who created them, and the last versions encountered at the time that the file was saved.

SOLUTION: Bundler/NPM/Cargo/etc for Recording?

This is something that so many languages have solved. Ruby has Bundler, JavaScript has npm, Rust has Cargo. (Incidentally, Cargo was built with involvement Yehuda Katz, who also worked on Bundler. Yarn, an alternative to npm that I really like, is another project that benefits from his involvement.) In each case, there’s an aspect that might be somewhat difficult for the recording world, a central repository of open-source libraries from which the depedency manager can draw, but I’d settle for just a list of what I need, how much of it is missing, and where I can get it. Baby steps.

PROBLEM: Shared Projects, Unshared Plugins

This one kills me. I’m working on a project and I want to send it to my friend to help with the mix or contribute some extra tracks, but I can’t because I’m using a bunch of plugins they don’t have. Even if we have the same DAW (let’s not even get into the fact that there isn’t a universal project format…), it’s going to be hard to share without coordinating very carefully. I know this is one of the reasons that UAD and Waves plugins are popular, but for the rest of us, it sucks.

SOLUTION: https://en.wikipedia.org/wiki/Adapter_pattern

The Adapter Pattern is a coding practice in which we create a trustworthy interface for something foreign and unreliable. It would work like this:

I open my DAW and I insert a generic reverb wrapper plugin. The reverb wrapper asks me to choose my reverb, so I choose some Nebula nonsense. I tell the wrapper, “this nebula variable is input, this one is output, this is wet level, this is dry level, this is delay time,” etc,… After that, I control my plugin entirely through the wrapper, not through Nebula. As I work, the wrapper keeps a record of the average output level, which will be important later. The next week, I send my project to my friend. She doesn’t have Nebula, but that’s ok because the wrapper will say, “Feed me a reverb. It must have a distinct input, output, wet, dry, and delay time.” She chooses some other plugin that satisfies those requirements. Because the project has been keeping track of the average output level, it does its best to tune her plugin’s settings to match mine. We’re obviously going to have different results, but it should at least get us started and allow us to communicate, which is better than where we are now.

There are a lot of problems with this. There are so few plugins whose settings would sync up nicely, I could see it being really frustrating. But maybe not. Or maybe some basic qualities would map over, maybe you’d have the shared settings and dial the rest to taste. Maybe something something machine learning could help find the settings in plugin B that are sonically closest to those of plugin A. I dunno, I’m just a guy who complains about technology.

I doubt that any of these will ever be explored, other than me maybe starting to use Git LFS to track versions of songs. They’re still fun to think about. As technology advances and we find better ways of sharing information, I’m confident that we’ll solve all of these problems in ways that are far more appropriate for the audio engineering world. Until then, it’s fun to daydream.

TypeScript Makes React Better

After starting work on the Proteus Client (Boston Biomotion) in the summer of 2016, it was clear that I’d be working fast, refactoring constantly, and experimenting with a lot of code. In an effort to bring some order to my work, I decided to go all in on TypeScript. I merged the PR that migrated all my js(x) to ts(x) on October 6 of that year. It was, without a doubt, the best gamble on technology that I can ever remember making. React’s simplicity and its preference for straightforward, clear, safe patterns makes it a perfect partner for TypeScript. I’m always finding new ways to get more out of them and cannot begin to imagine working on a large project without the safety net of the compiler. Below, I’ll share a few of my favorite patterns.

These all focus on a particular type of pain point that I run into when maintaining a large project: consistency and safety of objects coming from and going to disparate places. In other words: “How can I be sure that I have the thing that I think I have?” Most engineers in dynamic languages use a combination of tests, ducktype checks, trust, and a hell of a lot of grep and manual debugging. They keep a ton of stuff in their head and hope that everyone knows how everything works or is willing to trace stuff out when it doesn’t work right. I offer some examples of how we can make our lives easier by leveraging the compiler.

Better reducers, mapStateToProps, and component store access

At the beginning of the year, I wrote this post about TypeScript and Redux. It laid out my pattern for ensuring safety and consistency between the output of each reducer, the output of each mapStateToProps function, and the data accessed from within each component. In the eleven months (how has it been so long!?) since writing this, I’ve stuck with this pattern and truly love it. No change there. Read that if you haven’t already.

Better Redux actions

An omission in the aformentioned writeup was how to handle action creators. This was skipped because, at the time, I didn’t have a healthy pattern for it. You can see the evidence of this in one of my code snippets from that post:

// A reducer
function crucialObject(currentState = { firstKey: 'none', secondKey: 'none' }, action) {
  switch (action.type) {
    case 'FIRST_VALUE': {
      return { firstKey: 'new', secondKey: action.newValue };
    }
    case 'SECOND_VALUE': {
      return Object.assign({}, currentState, { secondKey: action.newValue });
    }
    default:
      return currentState;
  }
}

Note the implicit any of action. Note the trust that action.newValue would just… be there… and be what we expect it to be. Gross. In reality, this did not scale at all. My reducers grew to be frightening, messy places where data might be there, where I couldn’t be sure what keys were supposed to be present, where I couldn’t tell which action was responsible for which key.

There are a few libraries that try to solve this problem. I felt like they were complicated what should be a pretty straightforward issue. The pattern I settled on is nearly identical to the one outlined here. I differ in my preference for a slightly more manual approach within my reducers. While that post’s author likes a type that joins all possible actions, like this:

export type ActionTypes =
    | IncrementAction
    | DecrementAction
    | OtherAction;

function counterReducer(s: State, action: ActionsTypes) {
  switch (action.type) {
    case Actions.TypeKeys.INC:
      return { counter: s.counter + action.by };
    case Actions.TypeKeys.DEC:
      return { counter: s.counter - action.by };
    default:
      return s;
  }
}

…I name things a bit differently and just tell the compiler what each action is.

export interface Increment {
  type: ActionTypes.INCREMENT;
  by: number;
}

export interface Decrement {
  type: ActionTypes.DECREMENT;
  count: number; // to illustrate that sometimes, your actions might end up with weird, inconsistent keys
}

function counterReducer(s: CounterState, action: { type: string }) : CounterState {
  switch (action.type) {
    case ActionTypes.INCREMENT:
      const typedAction = action as Increment;
      return { counter: s.counter + typedAction.by };
    case ActionTypes.DECREMENT:
      const typedAction = action as Decrement;
      return { counter: s.counter - typedAction.by };
    default:
      return s;
  }
}

I like doing it this way because I think it makes it easier to quickly see what you’re working with in each case statement. It also makes it easy if you have an action without a payload, since you can be a little lazy and not define an interface for it. It takes a little more discipline but it’s worth it.

Either way, the result is the same: your actions will be consistent when they are created and read.

The Empty object and isEmpty

A tricky issue I ran into when first getting into this was dealing with empty objects. Say user reducer either returns a PersistedUser or nothing. How would I represent that? You can’t return undefined from a reducer and this:

function user(currentState: PersistedUser | {}, action: { type: string} ): PersistedUser | {} {
  ...
}

…is no good because AnyInterface | {} is treated as any, bypassing all type safety.

I settled on a simple pattern that I feel like I picked up in another language, but I can’t remember where. I define a simple interface that I call Empty:

export interface Empty {
  empty: true;
}

An Empty represents an object that is deliberately, explicitly blank. It might be a guest user, or a way to demonstrate that there is not a connection to the robot, or any number of processes that have not yet occurred. I define types like this:

export type UserState = PersistedUser | Empty;

And then export a very simple isEmpty function:

export function isEmpty(config: any) : config is Empty {
  return config !== null && config !== undefined && config.empty !== undefined;
}

By defining types that are either Empty or something else, I’m forced to always prove to the compiler that I’m acting on the right object. There are times where I use Empty when I could leave something undefined, since optional values are easy to cheat with !. I deal with the isEmpty case and move on.

A Better isEmpty() with Generics

An issue I ran into last week involved a refactor that allowed some bad code to slip through my isEmpty function. I started with this interface and type:

export interface Device {
  connected: boolean;
  ...some other things
}

export type DeviceState = Device | Empty;

I used it in components like this:

interface StateProps {
  device: DeviceState;
}

class DeviceAwareComponent extends Component<StateProps, object> {
  render() {
    if (isEmpty(this.props.device)) {
      return (something)
    }

    // go on with rendering happy path
  }
}

That was all well and good until I refactored that interface. I was left with this:

export interface ProteusState {
  status: ProteusConnectionStatus;
  device: DeviceState;
  errorMessage?: string;
}

export type DeviceState = Device | Empty;

// and back in the component

interface StateProps {
  proteus: ProteusState;
}

class DeviceAwareComponent extends Component<StateProps, object> {
  render() {
    // here's the problem
    if (isEmpty(this.props.proteus)) {
      return (something)
    }

    // go on with rendering happy path
  }
}

As you might notice, I forgot to change my isEmpty call to look at this.props.proteus.device. As far as my function was concerned, everything was fine. It has no awareness of whether it’s possible for this.props.proteus to be Empty, so it let it through, even when the device was in an invalid state. This was a pretty big problem and I needed a safer way of handling it.

My solution was to enhance the behavior of isEmpty with an optional generic that I can use to identify the expected interface of the object if it is not empty. By doing this, the compiler will do an extra check to ensure that what I think I’m passing is what is actually being passed. The code looks like this:

export function isEmpty<T = any>(config?: T | Empty) : config is Empty {
  return config !== null && config !== undefined && (config as any).empty !== undefined;
}

I can then modify the broken function call above and the compiler will bark at me immediately.

  // This fails to compile! The object passed does not match the generic given to the function.
  if (isEmpty<DeviceState>(this.props.proteus)) {
    return (something)
  }

I’m now using this everywhere that I call isEmpty without a clear sense of what the object should be if it is not Empty. Had this been in place ahead of time, it would have kept my bug from sneaking into my commit!

Bonus: better testing with rosie and generics

This isn’t specific to React, but with a little extra typing, TypeScript transforms the ease with which tests can be written in JavaScript when using rosie to create factories.

On the backend, I’m still trying to wean myself off of Ruby on Rails. My API is built with Grape and my responses are Grape Entities, but ActiveRecord remains my greatest addiction.

One of ActiveRecord’s greatest assests is the way it seamlessly maps your database columns to methods, creating getters and setters, and then offers these interfaces to factory_bot. There is immediate, guaranteed consistency throughout the stack, because the strongly typed database acts as a single source of truth for what is and isn’t permissible. Naturally, Ruby being Ruby, it’s not perfect. If you remove a column from your database, it’s on you to grep through your code and remove references to it, but ActiveRecord models are so easy to test via factories that it’s usually easy enough to get things passing.

This consistency is what gets me. My experience with testing in JavaScript always required a lot of dilligence. In pre-TypeScript days, if I had an implied interface for a PersistedUser, it was on me to ensure that my factory (if I had one) matched the actual implementation in production. After I started working with TypeScript, it was a little bit better because I could manually build factories using exported interface defs, but it was missing the fluidity of factory_bot and its integration with ActiveRecord.

I started working with Rosie a few months ago. With an interface inspired by factory_bot, it felt awfully familiar, except not: its TypeScript definitions made heavy use of any and its use of a shared global state made it hard to improve. I ended up reworking the definitions to allow the use of generics to let the compiler know what interfaces you’re defining or building. You can see examples here. We’re left with something that feels remarkably like the Ruby version.

In practice, you’d do something like this:

interface PersistedUser {
  id: number;
  createdAt: number;
  updatedAt: number;
  name: string;
  age?: number;
  occupation?: string;
}

Factory.define<PersistedUser>('PersistedUser').attrs({
  id: 0,
  createdAt: () => moment().unix(),
  updatedAt: () => moment().unix(),
  name: () => `${Faker.name.firstName} ${Faker.name.lastName}`
}).sequence('id');

// elsewhere...

const user: PersistedUser = Factory.build<PersistedUser>('PersistedUser', { name: 'Chris Grigg' });

In the above example, assuming you have the right dependencies imported, everything will compile correctly. If occupation suddenly becomes a required key in PersistedUser, the factory definition will complain. If the createdAt or updatedAt values went from unix timestamps to ISO 8601 strings, it would complain. If I supply the wrong kind of object to the second argument of build (maybe if I say { name: 666 }), the compiler will reject it. Same goes for adding a key that doesn’t exist.

Tests are worthless if they do not accurately match the code being tests. Without TypeScript and Rosie, we put the burden of maintaining parity on the user or a separate validation framework, which is a real drag. Introducing this change is a holy grail for me and has improved my test coverage dramatically.

Wrapup

So there you have it: some of my favorite non-trivial uses for TypeScript. These patterns let you build and refactor with significantly more speed and confidence than one could in vanilla JavaScript. The next time someone tells you that they don’t need a compiler because it’s easy enough to keep variable types in their head, share some of this with them and see how it compares to their process.

WiFi on Ubuntu 16.04 with wpa_supplicant trouble

I’m working on the first draft of the production version of the small server that acts as the brain for Boston Biomotion’s Proteus. Today, I hit a snag with the wifi that I want to document.

The unit we’re working with right now is the Intel NUC NUC6i3SYH. It uses the Intel WiFi 8260. We need to specify different wifi configurations for in-office and away, so I tried using wpa_supplicant in roam mode to specify our local and mobile settings with IP addresses. It kept hanging with a strange error and it took me way too many hours to get to the bottom of it.

The instructions I found everywhere were as follows:

Modify your /etc/network/interfaces like this:

allow-hotplug wlp1s0
iface wlp1s0 inet manual
  wpa-roam path-to-your-wpa_supplicant.conf

iface network-defined-in-supplicant.conf inet dhcp

# and so on...

This would fail to start at boot. The command sudo ifup wlp1s0 would fail with an error mentioning p2p-dev-wlp1s0 and, more importantly, wpa_bin daemon failed to start. The p2p-dev errors seemed more descriptive and pressing, so I troubleshot that for a while and came up empty.

I also found that if I started the service manually,

wpa_supplicant -B -i wlp1s0 -c path-to-config.conf

…it would start, but it did not have my roaming conf present so it was useless.

I finally started digging into /etc/wpa_supplicant/functions.sh, which contains functions invoked when someone calls ifup, among others.

I first modified the script’s call to enable more verbose output by appending -dd to the command, which wasn’t that useful, and then thought I saw somewhere that -D was another logging level. Strangely, adding just -D made everything work! It only took a second to notice that this specifies the “driver backend.”

Scanning a few more lines down, I noticed if statements that set different -D options. I execution to the path actually invoked and found that it looked like this:

WPA_SUP_OPTIONS="$WPA_SUP_OPTIONS -D nl80211,wext"

The docs for -D said that when given with no options, it defaults to wext. I modified the line to explicitly call wext without nl80211. My ifup command started working immediately. You can read up on the difference between wext and nl80211, but you can specify the driver to use from within your /etc/network/interfaces/ file. It looks like this:

allow-hotplug wlp1s0
iface wlp1s0 inet manual
  wpa-driver wext
  wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf

So, TLDR, add wpa-driver wext to your interface config file to use a legacy driver if the modern one is incompatible with your hardware.

Hope this saves someone else some time.

subscribe via RSS