Friday, 17 April 2015

This wavetable synth is done

Time to call it a day and say yes, this thing is feature complete.

Feature set :

Single EG per note (only used for overall level)
2 LFOs per polysynth
- LFOs have sin, saw, square, tri and S/H modes
- LFOs may optionally be synced to sequencer clock - the one feature not *quite* implemented yet
- one LFO is always on, the other is ramped up by the Mod wheel

2 layers of wavetable trajectories ('oscillators') per note
Each wavetable trajectory consists of up to 8 wavetables, traversed in one of
- linear time
- quadratic time t*t
- inverse quadratic time 1-(1-t)*(1-t)
- exponential time - half-life a go-go
The layers are traversed independently, so A may be linear, B may be exponential for example

Without an EG to drive trajectory there is just a dTrajectory/dt parameter. Trajectory resolution is always normalized to 1.0 irrespective of how many tables are in the trajectory, so a dTrajectory/dt of 0.5 will take 2s to pass through all the tables, unless the linearity parameter is set to exponential, in which case the 0.5 is interpreted as a half-life, so after 0.5s you will be half way through the trajectory list, after 1s at 0.75 etc. I will probably do one minor change here and should reciprocate  this so the the exponential half life is half of the linear traversal duration. Exponential is nice in one key respect, you always have some movement going on, no matter how long you hold a note. But obviously you are getting asymptotic to 1.0 after a while.

The trajectory traversal may also be modified with both the LFO and MOD LFO, with programmable sensitivities.
Pitch of oscillators may be modified by both LFOs with programmable sensitivities.
These LFO trajectory interactions are great for both slow movement and for crazy MOD-driven wiggling
Oscillator B may be manually detuned / uptuned by +- 12 semitones, with exponential control for fine detune
Oscillators are MIP-map antialiased, the octaves generated by FFT / de-FFT, and there's a secret sauce trick that can be manually enabled to preserve the phase of certain harmonics whilst retaining the critical (in my implementation anyway) feature of a positive-moving zero transition at table location 0 for all octaves.

There is a back-end LPF, which is a trivial 1st order thing but it does go truly transparent at half Nyquist. This is just one filter per polysynth, effectively a very coarse tone control to tune out the psychotic edge of the more extreme waves and bring in some 'mellow', if mellow is what you fancy. This is nice on lush ensemble stringy sounds to push them more background but leave their tones prominent to reinforce a melody.

There are some 21 built-in wavetables - I need to count, there may be just 17 - there are more than 16, I broke the nice power of 2 limit because I had some I couldn't bear to leave out. These include 3 Caitlin vowels, sampled piano, brass, alto sax, viola, and also analytical waves like saw, sin, square, tri.

The Cray XMP / SIMD programming system  is included, to support user construction of rich new wavetables from combinations of existing tables, be they built in or user-programmed.

Nice feature set, eh?

A first generation Raspberry Pi seems to be able to generate about 20 notes of polyphony from this synth, which is a lot of synthesis. So a completely reasonable thing is to create 4 PIANA Virtual Analog monosynths, 3 PIANATRON sampled synths and a pair of 4 note polyphonic PICYCLE wavetable synths, and let them go play.

Obviously on a Raspberry Pi 2 you can do all this and find a cure for the common cold in parallel using neat protein folding tricks. Or just fire up another dozen synths and go really crazy.

This thing sounds even cooler than I imagined, and to be honest I always expected it to sound cool. But it is BRILLIANT. Seriously.

Video to follow.

p.s fave trick to date - the Gordon Reid 'strings' trick with a Slightly Phase Distorted pair of different stacked sawtooths, one FMd by a 3Hz 0.08 semitone amplitude S/H LFO. Lush ...

Thursday, 16 April 2015

Wavetable synth programming - Cray-XMP style!

OK, that's stretching it a wee bit, but bear with me.

I wanted to expose a programming interface for wavetables that would let me ship with a relatively small number of tables installed - less than 20 - and let users construct their own wavetables of arbitrary richness and complexity via manipulation and combination of these 'primitive' tables. This would also mean that I didn't (necessarily) need to support an 'import wavetable' function with all the complexity of that - like having to publish a separate wave building tool etc.

Now, because the wavetables are 1024 samples wide, it turns out the simplest programming interface that I could think of for this was to treat an individual wavetable as a 'register', and perform only vector operations, 1024 at a time. And use a stack-based system so I can be groovy and reverse Polish. i.e. so I don't have to go to the effort of writing a parser.

So the instruction set for wavetable programming consists of this lot -

void init       (); // const reg = 1.0, zero out waves, zero out stack
void loadWave   ( const int slot, const short *samples );
void pop        ( short *here );    // retrieve results of all this work
void pushConst  ( const float c );  // const reg = c
void pushWave   ( const int slot ); // SP++ TOS = waves[slot]
void filter     ();  // take const as normalized freq and LP filter
void shiftLeft  ();  // take const as phase delta and rotate samples left
void resample   ();  // take const as 'speedup' factor and resample
void scale      ();  // stack[sp] = stack[sp] * const
void PD         ();  // phase distort stack[sp] by const
void offset     ();  // stack[sp] = stack[sp] + const
void triscale   ();  // stack[sp] *= triangular window for formants
void sinscale   ();  // stack[sp] *= sin window for formants
void mul        (); // stack[sp-1] *= stack[sp] sp--
void add        (); // stack[sp-1] += stack[sp] sp--

void sub        (); // stack[sp-1] =  stack[sp]-stack[sp-1] sp--

plus a trivial lexer that takes one line of text, strtoks it, and performs switch statements to select which operation to perform.  There are 4 vector 'registers' holding waves, so a new waveform can only be built from 4 others, but of course you can build temporaries then use those as subsequent building blocks.

And it's actually brilliant. I just threw away a couple dozen built-in waves - things like 8 differently phase-distorted squares and sines, and stacked saws with resonant formanty things on top - and in doing so simplified the innards of synth setup, and added this vector programmable machine to let me build these waves outside the application. And now I can craft great big wavetable noises without recompiling. About time too.

Thinking about it, it's a giant SIMD machine rather than a vector processor. Having once spent almost 2 years coding up a 3D pipeline and shader system on an 8,192-way SIMD machine you would have thought I might actually remember that key difference ...

Sunday, 29 March 2015

Wavetable update ...

I may need to grab a bit more video. I just tweaked the MIP map generation step, which enters the frequency domain and for each octave table, strips the high frequencies approaching and beyond Nyquist for this octave, then goes back to the time domain - all the while doing some secret sauce to aid with de-clicking and de-artifacting. I thought it was doing a great job already. But it turns out there was a pretty sneaky trick I needed to do in the secret sauce bit, which I only worked out by watching my triangle wave on screen - one of those proper "Eureka!" moments. And suddenly, everything with interesting / out of phase harmonics - the triangle, the various Caitlin vowels - now sound massively sweeter. Which is interesting, because it implies that I CAN hear phase relationships, and people aren't supposed to be able to do that. Not just that I can hear phase relationships but that they really are key to shaping these sounds. But anyway, once I've prepped a handful of interesting presets I shall turn on the video kit again. I'm fascinated in particular by how, over a small handful of notes, the 'Caitlin vowels' really sound like somebody singing Oooh or Aaah. Or at least, really sound like a speech synthesizer Ooohing and Aaahing away.

UPDATE (i.e. the author may have been having a partial senior moment)

Of course there is another issue I should not discount here - out of phase harmonics, blended over a wavetable trajectory, will cancel completely ...


Here's a thought I had today that I want to make a note of before I forget - instead of auto-computing wavetable octaves, what if I let the user supply them? Yes, I'll still have to FFT / de-FFT to kill frequencies beyond Nyquist, but it would let you capture the tonality of an instrument up and down its range. And suddenly you have a 2D surface of tables, indexed in X by frequency, in Y by time. That's a thought to not let go of ...

Friday, 27 March 2015


Finally - sounds. 

Unfortunately there is something amiss with audio quality here - the original on my Mac sounds great, but YouTube has done something to it and the saw sounds ugly. I may repost it with the volume cranked down to see if that helps ... and done, the linked video is now the newer version, which still doesn't sound as good as it should (i.e. as good as it does on my desk before I upload it) but is at least better than it was before. 

But ignoring the slight fizzy oddness with the saw, those swept formants really sound vowely and voicey. And they look great.

Monday, 23 March 2015

PICYCLE is now playable ...

... and it sounds insane. INSANE. The parametric sonic space is enormous for this thing, it's just ridiculous.

Sunday, 15 March 2015

25% CPU free

Being Boiled :

Endless repeat of 'worst section', all synths including lots of handclap.

Pi Model B+ single-core ARM11 950MHz
6 synths active
4 delays active (of 8 total)
reverb active (post-delay wet signal also has 'reverb send' amount)
sequencer active (separate application)
wave information published to sequencer via shared memory
wave display by sequencer @720p

Free CPU : thrash1 + thrash2 = 25%

I love that the sequencer is 0.3% of the CPU - just running 'top -H' is taking 6x as much! Last thing, shake out PICYCLE. Hopefully will get sounds before tonight's Toad Patrol ...

op - 13:04:39 up  3:03,  2 users,  load average: 2.67, 1.48, 2.25
Threads:  90 total,   3 running,  87 sleeping,   0 stopped,   0 zombie
%Cpu(s): 64.9 us, 33.2 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  1.9 si,  0.0 st
KiB Mem:    445740 total,   289020 used,   156720 free,    16560 buffers
KiB Swap:   102396 total,        0 used,   102396 free,   152432 cached

 3848 root      -7   0  118m  88m 3828 S  55.5 20.3   0:39.26 synth servic
 3853 root      20   0  118m  88m 3828 R  12.4 20.3   0:09.31 thrash2
 3852 root      20   0  118m  88m 3828 R  12.1 20.3   0:09.31 thrash1
 3857 root      -2   0 41004 5860 3144 S   8.5  1.3   0:05.94 glRender
 3846 root      -9   0  118m  88m 3828 S   6.0 20.3   0:04.34 nativeAudio
 3707 pi        20   0  4708 2420 2016 R   1.9  0.5   0:19.17 top
 3847 root      -8   0  118m  88m 3828 S   1.9 20.3   0:01.25 mixer
   31 root       1 -19     0    0    0 S   0.5  0.0   0:48.32 VCHIQr-0
 3858 root      -2   0 41004 5860 3144 S   0.5  1.3   0:00.39 VCHIQ completio
   30 root       1 -19     0    0    0 S   0.3  0.0   0:21.86 VCHIQ-0
 1539 root      20   0  1756 1096 1004 S   0.3  0.2   0:09.67 ifplugd
 3692 pi        20   0  9268 3524 2936 S   0.3  0.8   0:01.86 sshd
 3844 root      20   0  118m  88m 3828 S   0.3 20.3   0:00.19 WiFiprintf
 3854 root      20   0  118m  88m 3828 S   0.3 20.3   0:00.13 rawkeys
 3856 root      -3   0 41004 5860 3144 S   0.3  1.3   0:00.31 sequencer

Saturday, 14 March 2015

Pi day?

Nope. Totally, utterly not.

Pi day would be the 3rd of Quatorzember (or whatever the name of 14th month would be), not the 14th of March. Seriously, I'm British and proud of it, and today is 14.3 not 3.14

So expect an announcement here on the 22nd July. You know - 22/7

Of course it may only be an approximate announcement, or an approximate date, but watch this space. Things are happening.

p.s. this is cool - and demonstrates that an announcement on the 22nd of July is INHERENTLY A GREATER THING than one on the 14th March. Take that, you date-format-challenged, day-month inverted freaks!!

Friday, 13 March 2015

And while I'm waiting for a compile to finish on the Pi ...

... what is all this BBC Micro Bits about? I so don't get it. It's designed to teach programming - but requires a computer to program it. Huh?? Why not learn programming, you know - using the computer. They are quite good at that, everybody I know learned programming on a computer.

And if you don't have a full, proper, expensive(ish) computer at home, just buy something based on a Raspberry Pi. A starter kit can be had from various retailers for less than £50 including PSU and keyboard/mouse.

Really, it makes no sense to me on the basis of the small amount of information that's been released, I hope it all gets more meaningful when they do a full disclosure.

Still compiling ... will try to think of something else upon which I can off on one. Ideas on a postcard please.


There goes my hopes for a power of 2 line count.

Wednesday, 11 March 2015

64796 total

The result of typing

wc -l *.cpp *.h 

in my Pi build directory for PIANA / PIANATRON / PICYCLE

What if, at the moment I'm 'finished', this is exactly 65536? Wouldn't that be weird?

I'm hoping to get a video of my Raspberry Pi birthday bash presentation done before the end of Friday. Better make the next 256 lines of code work before then, otherwise the last slide but one will be telling a porkie.