OK, from the top - the synth application contains 3 different types of synth. These are -
Synth 1 : a Virtual Analog synth 'PIANA', a monosynth with 2 full-fat oscillators OSCA and OSCB plus a sub-oscillator OSCC. OSCA and OSCB support limited phase distortion - effectively PWM of all waveshapes, compressing the first half of each cycle and expanding the second to leave the period and the fundamental unchanged. OSCA and OSCB also support continuous morphing of the waveshape between 2 endpoints - e.g. square to sin, saw to square - under modulation matrix control. OSCA and OSCB may be voiced independently, with a range from 32' to 4'. OSCC is always a sin, and is always voiced at 32', so if A and B are both 32' it just reinforces them, if they are both 8' it sounds 2 octaves below them. Optionally OSCC may be used instead as a white noise source if sub-bass isn't needed or if you are making analog drums. Or explosions, or jet fighters, whatever. OSCA OSCB and OSCC levels are under control of the modulation matrix, so different EGs can apply to them, allowing for nice 'ensemble bass' sounds when OSCC is set to a slightly slower attack than OSCA and OSCB. OSCC frequency is hard-locked to OSCA, divided down by any difference in feet, but OSCA and OSCB frequency are under control of the mod matrix, which lets me do that great 'Virginia Plain' guitar sound of an octave sweep up on every note. Which is obviously honoured by OSCC as it is tied to OSCA. Hard sync of OSCB to OSCA is supported - nasty!! - and there is a ring modulator for programmable amounts of AxB - doubleplus nasty!!! The filter is selectable between a 'Moogy' filter (4th order resonant LPF) and a 'Minibruty' filter (a 'Hal Chamberlin' state-variable filter with taps for LP, BP, HP, notch and peaking). The frequency responses of the filter models have been carefully tuned against each other so both can be used to 'play the filter' or to just allow resonance to act as a self-oscillating extra sound source. There are 4 EGs and 4 LFOs feeding into the mod matrix. Modulation is rich and frankly a bit crackers, but it gives it a semi-modular amount of flexibility. The oscillators are all deep down in their bones just sin wave generators, and antialiasing of the oscillators is achieved by waveshaping, backing off from harmonic generation towards a pure tone when there is a danger of harmonics being generated too close to, or beyond Nyquist. It sounds great, even if I say so myself, yet manages to burn relatively low amounts of CPU, allowing multiple instances on a first generation Raspberry Pi or similar embedded platform.
Synth 2 : a sample replay polysynth 'PIANATRON' which is designed to be as lightweight and fast as possible. Two layers of samples are supported per note, with up to 12 note polyphony per polysynth. The synth honours the pitch bend controller, and allows Mod Wheel to be mapped to the layer blend control, and allows layer B to be either 'cross-fade' (removing A as B is ramped up) or 'mix' (simply adding in B). There is an EG available for overall level (to allow long decays on looped samples for example) and layer B may be detuned or uptuned by an octave relative to layer A. No filters, no modulation. There is no antilaliasing whatsoever, so if you play notes stretched to more than about a fifth above the sampled frequency, expect degradation and some energy to break on through from the other side. Dead simple, dead fast, but with good samples it really sounds brilliant, even more so with some subtle detuning between layers.
Synth 2.5 : let's not forget 'PICUSSION', which is PIANATRON for drums. Exactly the same engine as PIANATRON, but restricted to just one layer per note, and with modified polyphony management.
and finally Synth 3 : a wavetable synth 'PICYCLE' which supports 2 layers of wavetable sound per note, up to 12 note polyphony per synth. There is an EG per layer, detuning / uptuning of layer B relative to layer A, and in addition 2 LFOs, one always on (the amount is programmed into the preset) and one dialled in by the mod wheel. I have typically used the 'always on' LFO for really low frequency modulation of one layer, to give movement on otherwise static tones. Each layer generates sound by passing through a 'trajectory' of up to 8 wavetables at programmable rate, different rates programmable per layer, and an LFO may be dialled in to influence the trajectory. The wavetables are lerped / morphed as the trajectory progresses, so the sound of even a very slow trajectory is a seamless sonic journey with no tonal discontinuities. There is a small number (less than 20) of wavetables hard-coded into the synth, but the cool thing for tweakers is that additional waves can be constructed using the built-in SIMD wavetable construction kit, documented here a few weeks back. A total of 128 waves is allowed in memory per run of the application, a number that maps onto the 'midi byte' selection capability of a MIDI program change.
The feature set may sound somewhat limited - limited modulation, no filters - but it is really a very good, rich and flexible synth engine, excellent for glacial, early 80s digital polysynth pads and beds. With resonant / formanty sounds in the tables (fundamental-less harmonics, windowed) it can sound surprisingly analog as well. Antialiasing of the oscillators is achieved by multi-octave representation and frequency-dependent run-time selection of the wavetables, akin to MIP-mapping in 3D graphics, and the results are very good indeed. 8 octaves of tables are stored for each voice, the tables are 1024 samples wide, so the lowest table represents a 43Hz fundamental, the highest 11kHz, which is an octave and a bit higher than the highest note on a piano - there is no danger of out of control aliases with this range of supported frequency.
A really great and surprisingly flexible polysynth, big sonic space, very nice sound quality, and one that I have not yet had time to dig into hard to fully explore its capabilities. Too busy writing and performance tuning it ...
And while I'm on the subject of performance tuning - on a Pi 1st generation (A+, B+, A, B) overclocked to 950MHz (my standard setting) I can manage just about 7-8 PIANAs with the wind behind me, plus my Open GL ES accelerated wiggly waveform display. I get about 3x as many PICYCLE notes (some 20-24 maximum PICYCLE notes sounding), and at least 2x that many PIANATRON notes. These are all somewhat fuzzy approximates because with the standard kernel in place, once the system approaches 75-80% load ALSA justs fall over occasionally and causes glitching. Bad glitching, not art glitching. It's gruesome. A real RT kernel would help, ALSA seems to spend a lot of time failing and insisting on EAGAIN. I do hate ALSA. A simple 'DirectDAC' abstraction would buy back at least 5% of the CPU and firm up the ground I'm standing on - I hope Gordon at IQaudIO is reading this - 'DirectDAC' ...
But anyway, I digress - with this bag of tools polyphony can be traded off against programmability and flexibility, by picking the right synth for the job. But you are not necessarily trading off any sonic richness - a carefully-sampled instrument will add vast richness at very low CPU cost, albeit with little sonic flexibility. So this synth collection offers a giant sonic palette to exploit.
The app allows up to 8 of each type of synth to be instantiated, for what would appear to be total of 24 available synths, but the real limit is set to 12 synths. This is partly due to MIDI channel count constraints, partly for performance reasons on the Pi. And partly because I wanted to keep some MIDI channels free for future cool stuff.
Above and beyond the synths are 4 stereo delay engines which are shared between the synths. Each synth can select to send into one of these 4. In addition a single reverb is strapped across the whole system, and not only does each synth have a 'reverb send' amount, each delay engine also has a 'reverb send', allowing really wet sounds but also placing the delayed sounds into the same reverberant space as the dry synth sound, glueing arrangements together nicely. The delays and reverb each implement a simple 6dB/octave LPF on their input, with programmable cutoff frequency. The reverb sports configurable reverb time, the delays a configurable delay time for the left and right path, feedback levels for left and right. This means it can do the Brian May 'Brighton Rock' thing and more due to the feedback. Check out this short video for an example of a single PIANA monosynth being manipulated live with reverb and delay send amounts being twiddled. Giant and fab, and it's just a single PIANA, driven by a sequence so that my hands are free for knobs - a first generation Pi can do 7 or 8 of these depending on filter and oscillator settings :
(eagle-eyed viewers of a nerdy disposition will realize, after a quick inspection of the waveforms, that this video predates the introduction of DC traps into the synths ... fascinating, Jim)
There is a single inter-app / inter-thread MIDI port (accessible over ALSA using the virtual port name "SERVICE_MIDI") which allows a maximum of 16 different addressable ports and hence 16 different devices / synths to hang off it. The current setup allows MIDI channels 1 through 12 to have synths assigned to them - this is where the 12 synth limit comes in. Channel 16 is special, and 13 14 and 15 are currently reserved.
Channel 16 implements a configuration and plumbing engine, so the whole multi-synth system can be set up just using MIDI protocol. Messages sent down channel 16 do things like assign a synth of a given type to a given MIDI channel (e.g. 'activate wavetable synth number 2, set it up with 6 note polyphony, map it to MIDI channel 1, tie it to delay unit number 3'), and set up parameters for the reverb and the 4 delay engines. The sequencer allows all this to be programmed.
Once setup is done the synths are ready to accept normal MIDI commands down the newly wired-up channels to make music. As well as note on / note off / pitch bend / mod wheel the synths all respond to program change so that a keyboard with a program change button you be used to move between presets. All synths also respond to master volume, reverb send and effect send (controllers 91 and 92) for delay and reverb. The polyphonic synths respond to sustain pedal, and a bunch of other CCs are passed through for live tweaking. So with a MIDI controller that has enough knobs and buttons you can control everything from the keys. The application intercepts certain commands and chooses to act on them rather than route them - transport controls for example. In fact transport control are used to break out of 'repeat' statements in the sequencer. A sequencer program can set up a drum / bass break to last forever, allowing knob twiddling to mess with filters and oscillators, and hitting the 'play' transport control button breaks out of the repeat loop and allows the program to proceed. So if the crowd isn't digging the twiddling, break out of the loop early by hitting the '▶︎' button on the keyboard.
So, finally, onto the title of this post - 'Synths as a service'. Typically the app gets closed down and restarted every time I run it, which induces a pretty big startup delay as the big sample libraries get loaded into memory every time That delay can be even bigger if the demo is going to run a big MIDI capture file that not only needs loading and parsing before startup but also typically has a bit of captured silence at the start. So, by using the /service parameter and backgrounding the app with an & (yes, I should eventually do this internally and make it a daemon ... ) the app stays resident, listening to the ALSA virtual MIDI port and any connected USB MIDI devices, allowing 3rd party apps to instantaneously fire up and make music.
So looking to a future when this thing is done enough to get out there, if you have a MIDI binding available in your programming language that lets you see ALSA virtual MIDI ports, you will be able to directly code your own music with this engine.