This post is about implementing crossfading, for transitioning smoothly between audio segments or tracks.
Figure 1: Photo by Daniel Stone. Some rights reserved.
My motivation
I sometimes surf around Beatport to pick some tracks, most often categorised as tech house or deep house and usually by artists I never heard of before, and get them in WAV format to create a compilation in my taste. If a DJ had played the sequence of tracks, it would have been mixed together into an uninterrupted stream of music, but since I lack the skills and tools for that, until now I have just listened to them in sequence, allowing a bit of silence in between.
Recently, it occurred to me that since I use my own software to play the tracks, I could actually let that software join them together, while keeping the audio files unchanged. It wouldn’t be a proper DJ mix of the tracks, but worth trying. As a first step I could find a suitable endpoint for each track, and perhaps also omit a little bit at the start of the next one, to keep the rhythm going if the tracks had similar BPM counts, and make the transition sound as nice and musical as possible even if they didn’t. I could place the times in a configuration file that Klipspringer could adapt its playback to.
But I wasn’t quite satisfied with suddenly jumping from the soundscape of one track onto the next one. I wanted a smooth transition, like if you would quickly slide a crossfader control (like the one at the bottom of the image above) over from one turntable to the other…
Amplitude and power
So what would the code corresponding to a crossfader control do? Lets call the amplitude at time \(t\) of the fading-out signal \(v_o(t)\), the amplitude of fading-in signal \(v_i(t)\), and the mixed amplitude that we want \(v_m(t)\). It’s pretty obvious that we want \[v_m(t) = f(t)\cdot v_o(t) + g(t)\cdot v_i(t)\] for some \(f\) and \(g\) such that \(f(t)\) goes from \(1\) at the start of the crossfade section to \(0\) at the end, and \(g(t)\) goes the other way from \(0\) to \(1\).
But what exactly should \(f\) and \(g\) be? A first idea could be to just let \(f\) decrease and \(g\) increase linearly, so that if we say that crossfading starts at \(t=0\) and ends at \(t=1\), we simply have \(f(t)=1-t\) and \(g(t)=t\).
Let’s find out what that sounds like on a crossfade that’s long enough for really hearing it. The following lets the first half of the first verse of Ferry Man by Aurora Nealand and the Royal Roses1 fade out, while the second half fades in, so that it takes only eight bars instead of sixteen to arrive at the chorus from the beginning of the verse:
Doesn’t sound too bad, does it? But perhaps we can do better. You might notice a slight decrease in overall volume during the first half of the crossfade, followed by a corresponding increase back to normal in the second half. If so, that’s because the volume we hear isn’t really proportional to the amplitude of the signal, but more like the power of the signal, which is the amplitude squared. So to keep the volume constant, it makes sense to keep \(f(t)^2 + g(t)^2 = 1\) (instead of \(f(t)+g(t)=1\)). Let’s try \(f(t)=\sqrt{1-t}\) and \(g(t)=\sqrt{t}\):
Does that sound more right? I think it does, but hearing is subjective and obviously more complex than these simple formulae, so you if you prefer the first variant, I can’t say that you’re wrong. In particular, the theory behind the purported power-preserving variant actually holds only if the signals are uncorrelated, and strictly speaking they aren’t in this case: they are synchronised so that the rhythm lines up, which might make the drum beats and sousaphone blasts too loud. Indeed, if we mix together two copies of the exact same signal in this way, the result is preserved signal shape but with an increase in volume, followed by a decrease back to normal, with a maximum power of \(2\sqrt{1/2}\approx 1.41\) times the original in the middle.2
Note also that while the signal is guaranteed to stay in range in the amplitude-linear variant, the power-preserving variant calls for clipping of too-large or too-small values. For instance, if we have 16-bit signed amplitude values (like a CD does), values need to stay between \(-32\,768\) and \(+32\,767\), and if \(f(t)\cdot v_o(t) + g(t)\cdot v_i(t)\) is outside this range, the best we can do is clip it at the maximum/minimum.3
Exponential fade
Even if we decide that we want to have \(f(t)^2 + g(t)^2 = 1\) during the crossfade, the \(f\) and \(g\) used above are far from the only possible choice. There are infinite possibilities of choosing \(g(t)\) as any monotonically increasing function with \(g(0)=0\) and \(g(1)=1\), and then setting \(f(t)=\sqrt{1-g(t)^2}\). If the point is to fade in a piece of music that starts with an immediate drumbeat, like many of the tracks on Beatport do, \(g(t)=\sqrt{t}\) isn’t a particularly good choice.
Some of the functions illustrated in a Javascript context on a website called Spicy Yoghurt (where the topic isn’t really sound fading but animation easing, but that doesn’t matter) are good candidates. Variants of exponential functions are popular for audio fading, although for some reason they typically seem to be referred to as logarithmic (the inverse function of exponential). For instance, in Sox, a “logarithmic” fade-in is implemented with \(g(t)=0.1^{5(1-t)}\), motivated in a code comment by “5 means 100 dB attenuation”.
I’ve found \(g(t)=1 – 2^{-a\cdot t}\) to work well, where \(a\) is a positive constant that can be adjusted to make the fade-in more or less abrupt. It doesn’t quite make \(g(1)=1\), but setting \(a\) to at least seven or so, it’s close enough. Here’s an example that goes directly from bar eight in the Ferry Man verse to the chorus, with a crossfade of length 0.2 seconds, and \(a=7\):
This exponential function is now my default choice, although, as you will see below, not my only choice.
House mix transitions
So now let’s get back to some of the house tracks that were my original motivation. Here’s Freak Like Me by Lee Walker and DJ Deeon (Sonny Fodera remix) transitioning into Brooklyn Shit by Dario D’Attis in a 0.05 seconds exponential crossfade with \(a=20\). A very abrupt jump, very close to no fade at all, but it’s what I found necessary for the best-sounding transition in this case:
Metropolis by NTFO and Karmon starts a little more softly than Brooklyn Shit, so we can transition into it from Nighthawk by Mat.Joe a bit more smoothly, for 0.2 seconds and with \(a=8\):
When moving on from Brooklyn Shit into The Warning by Logic (Richy Ahmed remix) the BPM count increases too much for the beats to be mixed directly into each other, so I let one bar-length of the final cymbal of Brooklyn Shit be heard before softly transitioning into The Warning for a 0.15 seconds using the \(g(t)=\sqrt{t}\) crossfade variant:
Klipspringer changes
From the version tagged v2.4.0, when Klipspringer starts playing an album, it checks if the album directory contains a file data/mix.json. If so, it reads that file, and takes it as a specification for how to mix the tracks. This is the contents of the first mix.json I’ve made:
[
{ "end": "6:45.93" },
{ "fadein": ["0.05", "exp", 20], "end": "6:38.825" },
{ "fadein": ["0.15", "lin_pow"], "end": "6:04.10" },
{ "fadein": ["0.2", "exp", 10], "end": "7:13.63" },
{ "fadein": ["0.1", "exp", 12], "end": "5:56.57" },
{ "fadein": ["0.5", "exp", 10], "end": "6:46.11" },
{ "skip": "0.43", "fadein": ["0.2", "exp", 10], "end": "6:56.75" },
{ "fadein": ["0.3", "exp", 8], "end": "7:13.71" },
{ "fadein": ["0.2", "exp", 8] }
]
This means, for instance, that track 1 should stop playing at 6:45.93, and 0.05 seconds before that, a crossfade into track 2 should start. This is the transition example from above, going from Freak Like Me to Brooklyn Shit. The other transition examples seen are from track 8 to 9, and from 2 to 3. The skip element on track 7 means that the first 0.43 seconds of that track should be omitted (because it’s silence).
I changed quite a lot in the Klipspringer track player code to get the crossfading in place. Highlights:
- The fading functions are in instances of the Fade class, which is nested in MixSpec. There’s no documentation of what fade styles are available apart from the code, so see the if-else block near the end.
- The actual mixing, which uses the Fade objects, is in the write method of TrackChain, the class that’s gone through the most substantial changes. The way mixing works is that write notices when it has progressed into a crossfade section at the end of the track, and prevents the run method from playing that part of the internal buffer just yet. When it reaches the end of the track, it retreats the write position to the start of the crossfade in the buffer, and while the next track is in its fade-in section it gets mixed, sample by sample, with the data of the preceding track in the buffer. This means that the internal buffer has to be large enough to be able to hold the whole crossfading section.
- To be able to create the sound clip examples of this post, the track player can now output data to a file instead of audio output. That’s the job of LineFactory.
- Changes in TrackPlayer (some of which have later been moved to TrackFeeder) include modifications of the command line options.
- Since the configuration is in JSON, and JSON parsing is apparently not standard in Java yet, I unfortunately had to add another dependency to a third-party library: JSON in Java (package org.json).
Thanks
Thanks to mashup artist Johannes Müntzing for demos and input about fading function choices when discussing this topic on Twitter.
Notes
- I don’t hold the copyright for this or any other music in this post, but I hope that the fragments posted here can pass as fair use.
- This is a bit like resonance, which allows – for instance – a string instrument sound much louder than its strings alone, by having reflected signals of desired wavelengths line up in the resonance chamber.
- If many samples are clipped, it results in distortion. This is a problem with many CDs, due to loudness war.