Mixing music and electronics engineering always turns out to be more natural than it seems at embedded. For some strange reason, there is a surprising number of musicians at heart who found their way into engineering and ended up falling in love with writing code and drawing traces on a PCB. So you can imagine how much fun we had building a Dante-enabled product.
The product was Alltimix, Soundshell’s live monitor mixing system, where every musician on stage builds their own mix over Dante. At Focus we took care of the embedded side, hardware and firmware, while our friends at Edge Audio Labs built the software side of the system, from the application that talks to each unit to the apps musicians use to dial in their mix. You can read the full case study of the product here.
Dante is a networking technology, created by Audinate, that now lives inside a huge number of audio devices, and it exists to fix one of the worst nightmares of any audio technician: moving cables.
In short, it is a protocol for sending multichannel digital audio over standard IP networks. Picture a stage where, instead of a heavy multicore snake with one cable per channel running to the console, a single Ethernet cable carries a whole stage worth of microphones and instruments, and the devices on the network find each other and get routed by software.

Now, you, lover of old analog valve gear, might miss the sight of a wall of cables that looks straight out of an 80s show. But most live audio consoles are digital today, so converting the signal right at the stage and sending it over the network as data, instead of as a fragile analog voltage down a long cable, just makes a lot of sense. That is one of the reasons you see more and more products using Dante.
In this post I am going to tell you about our experience integrating Dante into an embedded product running its own application processor. I will walk you through the most challenging parts of developing the hardware it lives on and the firmware that controls the whole system.
Dante is a communication protocol that runs over Ethernet, so a Dante device is simply any device that knows how to speak it. The cables and switches in between do not care; what matters is what sits at each end. And there is more than one way to put a Dante speaker at the end of a cable:
In our case, the system was built around an application processor, so integrating DEP was the way to go. Let’s see what DEP asks from your hardware. On paper, three things:
But DEP is not a standard application you can compile and drop on any Linux board. It comes as a package tied to specific hardware, with its own kernel, a custom audio driver and a device tree that describes one particular board. Move away from that hardware and you are not integrating DEP anymore; you are porting it.

Most of the difficulties in building a platform for DEP are the usual challenges of integrating a SoM with Ethernet, the same ones any IP product has to go through. In our case there was one more: the product had to connect to five other devices over Ethernet, so the board also needed its own Ethernet switch.
That switch brought the first catch. With DEP, the kernel comes with Audinate’s BSP, so the switch has to be one that this kernel already has drivers for. Choose another one and you end up writing or porting a driver for a kernel you did not choose.
Then came the density. The processor talks to the switch over RGMII: twelve fast single ended lines that need matched lengths and 50 ohm impedance. On the other side, the five Ethernet ports add twenty differential pairs at 100 ohm. Fitting all of that, plus the clocks and the connectors in between, on a four layer board turned routing into a puzzle.
RGMII was the trickiest part: to move a gigabit over so few lines, it sends data on both edges of a 125 MHz clock, so the timing margins are tight. The clock has to arrive about 2 ns after the data, which in our case the chips handle internally, and every line in a group has to arrive together, which means careful length matching.
None of that works without real controlled impedance, so we agreed the stackup with the manufacturer before routing, choosing materials that gave us practical trace widths for 50 ohm single ended and 100 ohm differential lines, with the gigabit lines running right over a solid ground plane.

The other half of the story is time. All the codecs on the board share the same TDM bus, so they need to receive their master, bit and word clocks at the same time, and those clocks have to follow the network.
That is why the clock generator does not drive the codecs directly. Each clock goes through its own buffer, which sends a clean copy to every device, so the skew between them stays as small as possible. Jitter and skew on those clocks show up in the noise floor of the codecs, so they got the same care as the gigabit lines.
Implementing an application-specific BSP always comes with a few challenges. You work on the kernel version the BSP was built on, with the BSP owner’s proprietary drivers, and with a device tree written for their reference board. For a custom carrier board like ours, that meant adapting the device tree to our hardware and backporting from newer kernels whatever we needed, and the BSP did not have.

On Linux, the standard way to handle a managed switch chip is DSA (Distributed Switch Architecture): each port shows up as its own network interface and the kernel can configure it. That was our plan, but DEP did not work well with that model.
In the end, the switch runs as a plain unmanaged switch. A small service configures the port that faces the processor at boot, matching its Ethernet mode (gigabit, RGMII with internal clock delay), and from then on the switch forwards traffic on its own, PTP packets included, just like any standalone switch would. The tradeoff is that the processor does not see the status of each port individually, so if your product needs to report link status per port, plan for it early.
Then came the part that makes Dante, Dante: time. Every Dante device carries its own high quality VCXO, and all of them stay in sync over the network using PTP (Precision Time Protocol, IEEE 1588). One device is elected as the leader clock and the rest follow it. Dante uses PTP version 1, and version 2 when it talks AES67. The followers exchange timestamped messages with the leader, work out how far off they are and nudge their own clock until they match.
How good that conversation is depends on where each packet gets its timestamp. Stamped in software, it inherits all the jitter of the operating system; stamped in hardware, at the Ethernet MAC or PHY, it is taken right as the packet crosses the wire. That is why hardware timestamping matters so much on a Dante device, and why the network path matters as much as the code.
Getting PTP right took more time than any other part of the project. When sync would not lock, the problem could be in several places at once: the kernel, the way the system around DEP was configured, or the application itself, and the diagnostic tools gave us very little to work with. Part of it turned out to be a permissions issue in our own setup, which we fixed; the rest was trial, error and patience.
All the codecs on the board share the same I2S TDM (time division multiplexing) bus. In our case that meant eight analog inputs, four analog outputs and a headphone output. TDM simply gives each channel its own time slot on a shared data line, and it only works if everyone agrees on the format: how many slots there are, how wide each one is and where the first one starts.
The first sign that the formats did not match was not silence but noise on channels we had not even enabled. When the processor and a codec disagree on the format, samples land in the wrong slot or a codec reads bits that belong to its neighbor, and what should be silence turns into noise.
The real work was making the TDM format the codecs expected match the one the processor generated through DEP’s audio driver. DEP expects the codecs to be configured from user space, so we wrote small libraries to set slots, word length and gains at boot, and the processor side had to agree with every one of them. Since each datasheet describes its own part in isolation, we validated slot width, offset and format combinations on the real board, one by one, until the slots lined up.
Codec selection turned out to be critical here. Not every codec supports TDM, and ours were chosen for their analog performance, not for their digital interface. In the end, one of them does not speak TDM at all: it runs in plain I2S and still shares the bus with the rest.
Building a Dante device on your own processor takes more work than it seems from the outside, and a good part of it happens around Dante rather than inside it. DEP brings its own BSP, so the kernel, the drivers and even the way your switch is handled are partly decided before you write your first line of code, and you have to be ready to adapt. And since Dante only works if every sample arrives on time, the codecs are just as critical: you need to be sure of what each one does and of how the audio travels through every format along the way, from the analog input to a TDM slot, onto the network and back.
Add high speed signals sharing the same PCB with sensitive audio, and you get a design where routing, testing and noise have to be considered from day one, not after the first prototype.
If you want to see all the fun stuff on the other side of the cable, like the macOS and iPad apps where each musician dials in their own mix and the road that took Alltimix to NAMM 2025, go visit Edge Audio Labs’ website.
And if you are building an audio product, whether you are deep into Dante or struggling with embedded, don’t hesitate to reach out to Focus. We always love hearing about the cool stuff being developed in the audio industry.