Real Time Bass Whistling

July 2nd, 2019
contra, music, whistling
On Saturday I was playing around with Audacity and a proof of concept for converting the extremely high pitches of whistling into an interesting bass sound. When I tried pitch shifting programs live, however, they all had unacceptable levels of latency. Since a whistle is pretty much a pure sine wave, we don't need to shift the pitch, we can just detect it and synthesize a new one, right?

Here's what I have so far: mp3. The left channel is the bass, while the right is the input whistling. If you have a DAW on a Mac you could try the VST3.

How did I make this? I initially thought I should use Imitone, since that what it's built to do. While it's pretty neat, and does some clever things with interpreting vocals, I couldn't get the latency low enough there to be pleasant even with whistling.

I decided to try writing something myself. The first thing I figured out was that you'd normally do this with a Fourier transform. The idea is, you give it a bunch of samples, it tells you what frequencies are there. That's going to add latency, though, since the more precision you want on frequency the less precision you get on time.

Whistling is very close to a sine wave, so we can do better than that. Here's what our signal looks like:

Instead of complex processing, we can just count how many samples happen between zero crossings. Specifically, between one positive to negative transition and the next positive to negative transition. For example, in this case I count 27 samples in a cycle, and we're sampling at 44.1kHz, so that's 1633Hz.

This is fast, but it's not ideal. Consider these two zero crossings:

In the first case, the signal crosses zero very close to the last positive sample, while in the second the crossing is very close to the first negative sample. If we just count samples we can be off by a sample's worth. If this makes us count 26 samples instead of 27, that's 1696Hz instead of 1633Hz, almost a half step.

If we figure that the signal is approximately linear where it crosses zero, which is a good assumption for sine waves, then we can adjust by looking at last positive and first negative values. The larger the first negative value is compared to the last positive one, the farther back the zero crossing was. Mathematically, if n is the first negative value, p is the first positive value, and a is the fraction of a sample by which we should shift back our zero crossing, we have:

    p
     \
 ----------
       \
        \
         \
          \
           \
            n
      +--a--+
and so:
       |n|
a = ---------
    |n| + |p|

This makes our pitch estimates a lot more accurate.

I tried playing around with generating midi and controlling virtual instruments, but synthesizing a sound directly felt more natural. The basic logic is:

  val = sine(τ * fraction_of_period)
That is, if we're 0% of the way from zero crossing to zero crossing, then we emit zero, 25% we emit one, 50% zero again, 75% negative one, and 100% back to zero.

If we use (adjusted) samples per zero crossing as our fraction of a period we get a sine wave at the same pitch we're whistling. To bring it down N octaves we need to use a period that's 2^N times as long.

I'm currently mixing up a signal that's got bits of the original signal down five octaves, down four, down three and a half, and down three. This is kind of arbitrary and I should play with it more. Organ drawbars would be a good interface here.

I had been prototyping in python up to this point, but multiple sine computations for every sample was too much for it to keep up, so I ported things over to C here.

I tried for volume I'm using the average energy of the previous input period to determine the energy for the next output period, but currently I'm just using constant volume.

This mostly works, but can give crackles and unpleasant high pitched noises when the input frequency changes. The problem is that it suddenly jumps from one point on the sine wave to one that isn't very close.

I was able to mostly fix this by rethinking my approach so everything changed smoothly. I track "how far am I along the wave" and "what's my target wavelength in samples" and slide the wavelength toward the target. Similarly, instead of the wave coming in and out at full volume, I fade it.

There are still some crackles, and a low pass filter would work well. For now I'm doing some really simple averaging:

out = (out + 15*last_out) / 16;
This means that as long as each output sample is close to the ones near the signal is fine, but large fast shifts are heavily damped. This is basically a low-pass filter, which is fine since I'm trying to emit bass notes with periods in the 1000 to 500 sample (45 to 90Hz) range.

It's not a very good filter, though. Instead, I think I would just not do this averaging in my code and put a proper low pass filter afterwards.

I used iPlug2 to get it into Reaper and I'm pretty happy with it. Definitely needs a low pass filter, though.

The code is on github and uses PortAudio though I've only tried it on mac. I also have the VST in a separate iPlug2 repo.

Referenced in:

Comment via: facebook

Jeff Kaufman (6y, via fb):link

The video I uploaded isn't working because of the FB outage, but here it is on youtube: https://youtu.be/ngfN-bK5YIs

You'll want headphones or decent speakers; my phone speakers don't show the bass at all.

Jeff Kaufman (6y, via fb):link
Kathie (6y, via fb):link

This was fun to play with at the Greenfield dance! Hope Kingfisher comes back soon.

Jeff Kaufman (6y, via fb):link

I played with it more, and you're definitely right, there should be an attack. Here's what I have so far: https://www.jefftk.com/bass-whistle-percussive-attack.mp3

Jeff Kaufman (6y, via fb):link

I'm still learning how to play it, but it's definitely possible to play it in a percussive way. Here's a short snippet from Greenfield last Friday: https://www.jefftk.com/bass-whistle-percussive-example.mp3

I may play with the synthesis to change the
attack properties to make it stick out even more, but there's a difficult triangle here:

* The sharper the attack, the more important extremely low latency is.

* The sharper the attack, the more important it is to avoid mistriggers (it thinking I'm starting to whistle when it's actually some other sound)

* The most powerful way of avoiding mistriggers is to wait for more samples before declaring whistling to have begun, which increases latency.

Mostly I need to experiment more, and learn to play it better.

Bob (6y, via fb):link

that's cool and i'm glad you got it working. for me as a dancer/musician, attack is an important part of bass, and is why historically bass and percussion have been linked in dance & pop music. so this doesn't turn me on rhythmically, because it has no attack, it's just a low sound, which i actually find annoying in the form of didges, and i usually leave the room soon after that starts up, especially if the sound tech has subwoofer abuse syndrome. it's cushioned a little bit here by your great mandolin chops, but that's really a bandaid on the problem. just one person's opinion...

Jeremy (6y, via fb):link

Maybe? I dunno, my computer can run Mojave (I just haven't had the chance to upgrade). I occasionally run into software that requires a more recent OS, but I've never had something say it requires a more recent processor. Specifically I've probably downloaded a dozen plugins over the last year and no problems. Anyway, don't spend too much time on it on account of me. I will probably be getting a newer computer in the next 6 months. That clip you posted with more attack sounds great though - surprisingly convincing electric bass sound.

Jeff Kaufman (6y, via fb):link

Jeremy I probably need to build for an older processor. I'll try to figure out how to get xcode to do that.

Jeremy (6y, via fb):link

Thanks for looking into it more. I have a mid-2012 MacBook Pro. I'd imagine it would be an issue of OS version before hardware but I guess you never know. I realized I have a Parallels VM of Mojave so I tried installing it within Reaper in that. It showed up but then it crashed when I loaded it on a track. Weird. Anyway, I wouldn't ask you to spend any more time on it, but if you do want help troubleshooting feel free to email me (not on FB much these days). My email is my name with my middle name "guy" in between first and last at gmail.

Jeff Kaufman (6y, via fb):link

What mac model do you have? You can see this in Apple Menu > About this Mac. I'm building on a "MacBook Pro (15-inch, 2017)", and maybe I need to change something about how I build it to get it to work on older macs?

Jeff Kaufman (6y, via fb):link

After talking to the iPlug2 folks, "Distributed" doesn't have to do with distribution but with splitting code between multiple processes, so I don't think that will fix it

Jeff Kaufman (6y, via fb):link

Jeremy I tried building "VST3 (Distributed)" instead of "VST3". Does this work: https://www.jefftk.com/BassWhistleVST3-current.zip

Jeff Kaufman (6y, via fb):link

I'll try posting on the iplug2 group

Jeremy (6y, via fb):link

yeah no different.

Jeremy (6y, via fb):link

I'll try it in the user folder as well but my understanding is it shouldn't matter.

Jeremy (6y, via fb):link

Hmm not sure. Happy to test that for you if you want. Thanks for checking.

Jeff Kaufman (6y, via fb):link

I wonder if maybe I need to build "VST3 (Distributed)" instead of "VST3"?

Jeff Kaufman (6y, via fb):link

I have mine in /Users/jefftk/Library/Audio/Plug-Ins/VST3/BassWhistle.vst3/

Jeff Kaufman (6y, via fb):link

Jeremy the VST works on my Mac, but I've never distributed a VST before so I might be doing something wrong.

Jeremy (6y, via fb):link

Seems very cool. Just want to make sure I am understanding this right. The VST3 download should just work in any Mac daw, right? I am trying in latest Reaper64 under Yosemite and it's not showing up in my plugins list. I put it in /Library/Audio/Plug-Ins/VST3/. Tried re-scanning VSTs. I don't expect hand-holding or anything, but did you test the VST3 file as you uploaded it?

Recent posts on blogs I like:

Book Review: Daygame Mastery

the dark, forbidden, secret truth that women like men with boundaries

via Thing of Things April 18, 2025

Which Came First, the Chicken or the Egg?

When I thought about this question it was really hard to figure out because the way it's phrased it's essentially either a chicken just pops into existence, or an egg just pops into existence, without any parent animals involved. I thought about t…

via Lily Wise's Blog Posts April 13, 2025

Advice for time management as a manager

have accurate expectations of yourself • prioritize ruthlessly • unemploy your future self • a five-step “help, I’m overwhelmed” checklist • carve out focused time

via benkuhn.net April 1, 2025

more     (via openring)