Terrarium Polyphonic Octaves Code & Demo

I got another one for y'all. This time we're doing polyphonic octaves, like a POG or a Sub 'N' Up.

Project page: https://github.com/schult/terrarium-poly-octave
Firmware binary: https://github.com/schult/terrarium-poly-octave/releases/download/v1.0.0/TerrariumPolyOctave.bin


Not the best demo, I know, but I'm wiped out, and I want to get this thing out there. I also probably could have done a better job matching the settings of the Sub 'N' Up with the Terrarium. Regardless, my take is that my implementation landed pretty close to the Sub 'N' Up, but the latter still has a little extra low end resonance that my version is missing. I kind of wonder if TC didn't just include a tiny bit of reverb/delay at the end of their algorithm.
 
Ah, was not aware of that particular octaver algorithm. Sounds really interesting. Did you derive that code from the paper or was it already published somewhere?
 
Ah, was not aware of that particular octaver algorithm. Sounds really interesting. Did you derive that code from the paper or was it already published somewhere?

All the code outside of the lib directory is my own*. I'd say I followed the spirit of the paper, but I didn't end up slavishly copying every detail it specifies. I'm not aware of an existing implementation published anywhere.

More information than you bargained for inbound...

ERB-PS2 Algorithm​

The paper that passtheducky mentioned, which describes the ERB-PS2 algorithm: https://core.ac.uk/download/pdf/80719011.pdf

To summarize ERB-PS2 for everyone, the dry signal is fed into a bank of narrow bandpass filters in order to isolate individual frequencies (and their phases). This is similar to what an FFT does, but it allows unequal frequency bin sizes, and doesn't require evaluating the entire spectrum. Each filter output is then shifted to produce a new frequency double (or half) of the original, and the results from all filters are mixed back together.

The ERB part of the name refers to how the filters are spaced using a model of how the human ear receives sound.

The PS2 part of the name refers to "Phase Scaling by 2", the process used to shift the frequency. In my opinion, this is the key innovation over the Rollers algorithm, which used single-sideband modulation to perform the frequency shift.

What I Changed​

The actual "Phase Scaling" equation that I used is from the paper (page 29). That said, the paper only covers octave up, and if you expand the equation for octave down (γ = 1/2), you'll see that you end up with a square root, which needs to alternate between positive and negative for each cycle of the original signal. The good news is that we already have a quadrature signal, which makes detecting these transitions fast and easy. At least, easy once you realize it. I don't have a ton of signals background, and it took me a while to figure this out.

Like the paper, I use a bank of narrow-band filters that generate quadrature signals. I used a different bandpass filter design than what was specified, mostly because it was the first (and only) thing that I got to work well.

For the filter spacing, I ultimately gave up on worrying about the constant-ERB-bandwidth concept, which didn't produce the results I wanted (but maybe I was doing it wrong?). Instead, I did the same thing with the Sub 'N' Up that the author of the paper did with the POG - I fed it a frequency sweep and analyzed the output using an FFT. This convinced me that the SNU is using the same algorithm, and it revealed a couple of key ideas:

First, the SNU completely ignores any input frequency over about 2 kHz. This greatly reduces the number of filters needed. It also allows downsampling before performing the octave shifting. Both of these features proved to be essential for the amount of processing power available on the Daisy Seed.

Second, the SNU filter spacing is tighter than what I understood the paper to be recommending. Its filter placement more or less follows an exponential curve that is positioned so there are fewer filters per semitone at the lower frequencies. This keeps the low-frequency filters from getting too narrow, which would introduce more delay. I followed a similar curve for my own implementation.

The very lowest filters of the SNU are placed to space them out even further, and some are excluded from the down-shifts where the result would fall below 20 Hz. I did not replicate these details.

I think that covers it.


* Correction: the fast inverse square root function is cribbed from elsewhere; I forgot about it when I was writing this.
 
Last edited:
How does the latency compare to a POG? Judging from the demo, it looks like it’s really snappy based on your fingers and the audio.

The POG is the only pedal I use where I have yet to find a good DIY alternative. This is awesome work.
 
How does the latency compare to a POG? Judging from the demo, it looks like it’s really snappy based on your fingers and the audio.

An impulse test shows a delay of about 4 ms, which is very close to what was reported in the paper for ERB-PS2. My measurements of the Sub 'N' Up were similar: about 4 ms.

I don't have one to compare against, but the paper reported delays of 15-20 ms for the POG. But there were significant differences in behavior between the original POG and the Micro POG. This makes me think that other POG models might have changed up their algorithm as well, so they could be faster.

Keep in mind that the latency may be different at different frequencies, and lower frequencies are likely to be slower. But subjectively speaking, I'm very happy with the response time across the full range of my instrument. It feels good to me.

The POG is the only pedal I use where I have yet to find a good DIY alternative. This is awesome work.

Thanks!
 
Amazing work. Great explanation of the algorithm too. I have some Daisy pedals I’ve made. I can give it a whirl at some point.

It’s interesting that it doesn’t shift the high end. Just focusing on the fundamentals and maybe the first few harmonics must really mellow out the effect. Would probably work great as an octave fuzz for a signal that already has plenty of sizzle.
 
So... there's a companion webpage for the paper, which features a few recordings and the results of feeding them through various products/algorithms. Now I've put the originals through my pedal as well for comparison - links below.

All my files have been normalized but are otherwise unedited. For the up/dry blend, I set the knobs so the result sounded in the same ballpark as the other recordings. It's hard to make an apples-to-apples comparison there, since my pedal drops all higher frequencies on the shifted signal and adds some EQ on top of that.

 
I’m planning on building a terrarium that’s purpose-built for running this patch- with only the controls needed attached.

As someone who hasn’t used a daisy seed yet, I was wondering if this patch would run fine on the daisyseed with 1MB memory.
 
I’m planning on building a terrarium that’s purpose-built for running this patch- with only the controls needed attached.

As someone who hasn’t used a daisy seed yet, I was wondering if this patch would run fine on the daisyseed with 1MB memory.

You should be fine with the 1MB version. Here's the reported memory usage for my code (see How to determine your memory (RAM) needs):
Code:
Memory region         Used Size  Region Size  %age Used
           FLASH:       75576 B       128 KB     57.66%
         DTCMRAM:           0 B       128 KB      0.00%
            SRAM:       14060 B       512 KB      2.68%
          RAM_D2:       16704 B       288 KB      5.66%
          RAM_D3:           0 B        64 KB      0.00%
     BACKUP_SRAM:          12 B         4 KB      0.29%
         ITCMRAM:           0 B        64 KB      0.00%
           SDRAM:           0 B        64 MB      0.00%
       QSPIFLASH:           0 B         8 MB      0.00%
 
Back
Top