Skip to content

Desktop speakers, an exercise in avoiding feature creep

Contrary to hackENS, the hackerspace where I work right now does not have a woodworking space, which makes one itch for such projects. Being back home for one week for unrelated reasons, the obvious thing to do was a woodworking project! Besides, there's nothing like spreading chips everywhere and triggering smoke detectors with a friend.

But what to build? My woodworking yearning collided with some frustration about my current audio setup (read: headphones made from old office phone parts and laptop speakers) and cheap drivers stocked by my uni's electronics supplier. A project was born!

Thinking first about the requirements, I got to the following wishlist. The speakers shall be:

  • as simple as reasonable, because they'll be built in a week

  • somewhat portable and rugged since I expect to be moving around in the next few years

  • provide sufficiently high fidelity that I'll enjoy listening to them. A nasty side effect of listening to a lot of music and toying with audio gear is that one gets increasingly picky regarding audio quality.

This suggests building desktop speakers. Since they need to provide adequate performance, they'll have a sizeable footprint on a desk. Too large a footprint wastes desk space, but too small is not better! I settled on a roughly A5 footprint. That way, the top of the speakers can be used in a pinch for a notebook or some pens.

Mechanical planning

The footprint of each speaker is roughly 15x21 cm. For a nice aspect ratio, the height will be around 21 cm as well, so the internal volume is 6.5 liters. With a sealed enclosure, the low frequency cutoff is given by the compressibility of the trapped air making the driver work harder. Drivers have an internal stiffness which they work against, and compressing the air in the box effectively raises that stiffness, making their cutoff resonant frequency higher. Helpfully, driver stiffness is specified in the equivalent volume of air, in liters. As long as the characteristic volume of the driver is well below the internal volume of the box, the total stiffness is going to be dominated by the driver's and we won't spoil its low frequency response.

Knowing that, we can look for drivers. They need to be all-range, with a reasonably low cutoff frequency and an equivalent stiffness volume less than a few liters. Working through the Visaton catalogue sorted by increasing price, I settled on FRS 8 drivers. As shown in the emission diagram, in the upper octaves the sound beam width is quite poor, which does not matter. Remember the requirements! These drivers have a resonant frequency around 120Hz, which the box will raise to around 130 most likely. This might be disappointing, but we can manage with some trickery, so read on.

emission diagram

Some of you might be asking why I'm chosing a closed box design: most cabinets feature either a hole or a second, passive driver. I've even been told that all speakers have to have that to let the sound out! By having a hole or a membrane to relieve the pressure differential, these open designs can extend the frequency response lower. However, writing down the mechanical equations, they turn out to have order-4 response, meaning that below their cutoff, their output drops by -24dB/octave (contrary to the -12dB/octave rolloff of sealed boxes). And with such small boxes, some of that cutoff will be in the audible range anyway... Furthermore, to correctly extend the response, the size of the hole has to be computed precisely relative to that of the box. I don't know how to do that and anyway my woodworking skills are no so precise as to allow such a design to reliably work!

We now have an idea of what we'll build: two one-way sealed-box speakers, with dimensions circa 15x21x21cm and using FRS 8 drivers.

Amplifier specifications

The drivers have a sensitivity of 82 dB @ 1W, 1m. Aiming for a generous listening volume of 72dB, this results in a typical driving voltage of 0.9V (if using the 8Ω variant) or 0.6V (if using the 4Ω variant). However, remember that we have mediocre bass response. We'd like to extend it one octave or two! Since the rolloff is -12dB/octave, if we want to extend the response by one octave, the incoming signal has to be amplified by +12dB if it is low frequency. This roughly translates to peak driving voltages of \(4 \times 0.9 \sqrt 2 = 5.1 V\) if using the 8Ω version, or \(4 \times .6 \sqrt 2 = 3.4 V\) with the 4Ω version. This tells us two things:

  • We are not going to be able to adequately drive the speakers from a regular headphone output (this is not a problem, these are desktop speakers!)

  • Since we're using an amplifier anyway, we might as well use the 4Ω variant which will be easier to drive, and for which we can extend the response lower, all things being equal.

To simplify power supply design, the amplifier will be powered by a standard voltage, 12V. We could get it from USB-PD if we needed, but hackENS has a bunch of old barrel jack power supplies anyway.

The electronically-minded will then wonder what kind of amplifier I built. Our two options are class AB and class D. Class D is more efficient and could probably give adequate performance if we fiddle with it enough. On the other hand, class AB is simpler, especially since it's going to be built on a protoboard with a poor ground plane. An unofficial requirement of all projects is, after all, not being an EMI bomb. Class AB is less efficient (typically 60%, compared to near perfect efficiency of class D), but that's not a real problem: without the bass extension, we're driving the speakers with only \(.1W\), if that, and the bass boost is not going to increase the overall power much.

amplifier schematic

(see the full KiCAD schematic in the repository)

I opted to not include a volume knob. To ensure the final driver stage always sees the same impedances, that would have needed two additional op-amps, so one more chip, complexity, and brittleness as potentiometers tend to break. The user will plug in a laptop, which has more than enough ways to adjust volume already! In the same vein, after much thought, the bass extension circuit was relegated to software. That way it is more easily tuneable and more importantly it reduces hardware failure or drift possibilities.

I had some time to design the circuit before coming home, so it does have some niceities. Foldback current limiting protects the power transistors should the output capacitor (likely) or the speaker (unlikely) fail short-circuit. Things will get hot, but not so hot they'll have to be replaced. The DC output level is settable for maximum swing, and the control loop is bandwidth limited for stability. The RC snubbers at the output were added after building the amplifier -- the output stage turns out to have plenty of gain up into the megahertz, which resonates with the LC tank formed by the speakers and their wire! When building such circuits, paying attention to return current loops is a useful skill to have. I'm sure there would've been much more crosstalk and instabilities if the power current loops were not tidy.

amplifier

Woodworking

Now onto the star of the show, the woodworking. HackENS' CNC was considered defective (it may not have been, but that's another story), so this would be all manual. The two cabinets were assembled from one sheet of 120x60cm 15mm chipboard, see the attached drawing:

cutting diagram

The green rectangles are router indents, 10mm deep or so. They make shoulders for the front and back panels. As a bonus, they provide a tiny shroud to protect the driver and connectors.

I'm fairly happy with the efficiency of this cutting diagram. The large horizontal and vertical cuts were chosen in such a way as to in theory make the box fit well, even if we fail to get the exact dimensions. In that kind of job, it is important to draw the cutting lines such that they are parallel to the edge of the board, not perpendicular to the edge they're coming out of. This is because while the board is nominally rectangular, in practice it's not. Drawing the lines the naive way leads to a variation of up to a centimeter by the time the line reaches the opposite side, a ticket to a very long sanding session. The order of the cuts is as follow: cut the horizontal strips containing the sides and the top/bottom, route them, then do the vertical cuts to free each pannel. The front and back are cut last.

HackENS does not have a circular saw, everything was cut using a jigsaw. The first cut we made was the long horizontal one. And it went wrong. Using a hacksaw for such a large cut, if one side of the board is not supported, it will flex as it is cut. The thin jigsaw blade warps and the resulting cut is curved, even though the saw's body describes a perfect line! Lesson learned: support both sides of a board when cutting it. We decided to leave the cut as-is when we saw it was 4 millimeters off (yikes), and cut the other cabinet first. When I came back to the first cabinet, the top and bottom panels, which were the most affected, were redrawn using some spare wood from the board.

first fit gluing, and praying The fit was fairly good on the first try, sanding was minimal. On both cabinets, the front and back had to be sanded so their height matched the walls'. Paint was applied on the front and back panels before gluing, since it would get hard to paint around the drivers and connectors once everything was assembled. On the back sides, to get the connectors to fit despite their limited height, I routed pockets around the holes. They're ugly, but invisible once the box is assembled.

Once the glue was set, to cover the ugly inside ridges, I applied blue paint to the inside edges of the cabinets. After hesitating between varnish and white paint, I and my friend decided to varnish the cabinets, leaving the chips visible. Given the wood we're working with, it is very important all accessible surfaces have some kind of varnish or paint, otherwise brushing hands will have a bad surprise. Making sure every ridge is painted over also helps caulking up potential tiny holes in the box where the glue did not flow.

The varnish dried really thin, it is almost invisible. That was a pleasant surprise as it had a peanut butter texture and opacity at first. I may add more coats later if it turns out to not be enough, but I'm hopeful.

varnish drying varnish dry

Software Linkwitz equalization

I moved the problem of bass response from the mechanical to the electronic domain by having a small closed box, and then from the electronic to the signal processing domain by not building the bass extension into the amplifier. I couldn't run away anymore and had to deal with it in software.

I repurposed some code for a previous project (turns out that when dealing with audio, simple filters are a common thing to implement). To nicely integrate with my setup, it runs off pipewire's API. Pipewire offers a pw_filter interface, which in theory is exactly what we want. It takes care of buffering the input and output ports, only the actual filtering needs to be implemented. Perfect! Unfortunately, as far as I can tell, it does not support multiple channels per port, i.e. stereo... A way out of this is to manually describe multiple ports, but this gets messy quick. The result also loses the ability to connect easily to the remainder of the audio system, which makes it a bit too janky even for my taste.

I turned to the lower-level pw_stream interface. That means we do have to take care of buffering, skipping ahead if an output is disconnected and so on ourselves. For such a simple application, most of the code ends up being bookkeeping and negotiation rather than interesting stuff... This project would have been a perfect use of pw_filter if it supported multiple channels per port!

With the boring stuff taken care of, we can look into the equalization proper. The idea is to replace the order 2 rolloff (given by frequency \(f_0\) and damping factor \(Q_0\)) of the speaker by a new rolloff \(f_1, Q_1\) with \(f_1 < f_0\). If we get it right, the combined system will exactly behave as if it had a rolloff given by \(f_1, Q_1\). This technique is called Linkwitz extension. The transfer function we want to get is:

\[H(s) = \frac{s^2 + \frac{2\pi f_0}{Q_0}s + 4\pi^2 f_0^2}{s^2 + \frac{2\pi f_1}{Q_1}s + 4\pi^2 f_1^2}\]

This is why I coded my own equalizer rather than using one off-the-shelf. A general purpose EQ splits the signal into frequency bands, multiplies each band-limited signal by some gain, then combines everything again. This is more flexible of course but getting the exact response I want is more complicated.

The response above is continuous-time, it has to be converted to discrete-time since we're implementing it in software. Since it only does things at rather low frequencies (hundreds of hertz) compared to the sampling frequency (44.1kHz or above) the conversion does not distort the response too much. To find the discrete-time coefficients, I used the bilinear transformation. The resulting filter passes high frequency signals intact and amplifies low frequency signals. As-is that means clipping. We need to figure out what is the peak amplitude of the output signal when the input signal has peak amplitude 1. As far as I see, that's not something looked into often but we can derive an explicit formula for this quantity called the 1-norm of the impulse response. I have made a technical note for the curious.

As an aside, this made me understand why bass boost in sound equipment is an option. I thought it was because it made the total bass response lounder than the treble one, therefore reducing fidelity, but it probably strives for extending a flat response like I tried here. To do so, like we've seen, it must lower the total loudness, which can be a significant tradeoff depending on the situation. The nuances around clipping were a factor towards me implementing the filter in the digital domain. The analog domain equivalent of the 1-norm is more complicated to compute as far as I can tell.

Summing up

Building everything during spare time in a week proved a bit too ambitious; I was almost able to complete everything, except for putting the amplifier in a nice box. Anyway, the whole setup is a bit too much to carry around in a bus, so it'll stay in Paris until I return. What was built however does satisfy the requirements:

  • the build was almost completed, and the sound quality assessed. Having the equalizer in software does bring some complexity for the user, but integrating the Linkwitz extender in my software stack is not a problem for me.

  • the 15x21x21cm enclosures are compact enough for my needs, moving them once a year or so is not going to be a major problem. They are not portable though, weighing 2kg each. Given the simplicity of the hardware, I expect it to live at least a few years, and probably until I decide to upgrade it.

  • the sound quality is very promising, even before bass extension. With bass extension tuned (I did not have the time to measure \(f_0, Q_0\)) I expect to bring the cutoff to at most 60Hz, which with the -12dB/octave rolloff will be enough for all except the boomiest music. With the bass extension disabled the speakers are loud enough to be heard over a room (or two) so they can be used for that kind of service in a pinch.

I really liked this project. It is self-contained, serves a need, and was an opportunity to exercise a broad range of skills while balancing complexity between all of them. Like the title of the post implies, I feel the successful completion of that project owes a lot to sticking to what's actually needed. A tweeter would have made the project much more complex, potentiometers break. Class D would have been nice, but complex, and its added efficiency would not have mattered. I considered adding deadening to the enclosures (compressible material damping box resonances) but figured out it wouldn't matter here, the box being so small and the room acoustics being far from ideal anyway. Sticking to what's needed does not mean doing the bare minimum required: I could have ditched the EQ part and still have decent speakers, and a simple fuse would have made sure the amplifier doesn't burn down the house, although it could not have effectively protected the transistors at the same time. I feel it's more about balancing niceities between "actually useful" and "gimmick that does not matter (much)". Note that these categories depend a lot on the use cases! The software equalizer for instance would make no sense if I needed an AV system more complex than a computer.

The full schematic, as well as the equalizer and some notes are available on the repo.