Let's design and build cool (but expensive) FPGA based theremin

Posted: 10/18/2018 5:37:05 AM
Buggins

From: Porto, Portugal

Joined: 3/16/2017

Idea of classic analog theremins simulation on FPGA digital theremins (based on oldtemecula's suggestion).

* Prepare schematic of analog theremin in LTSpice.
* Record oscillator waveforms and prepare wavetable(s) with them.
* Make model of the rest of schematics (mixer, waveform corrector/filter, VCA).
* If VCA doesn't add any distortion, it may be just replaced with multiplication by volume (and volume antenna part may be removed).

Modelling must be performed with frequency higher than oscillator frequency, e.g. 100MHz step for 600-1000KHz oscillators.

Posted: 10/18/2018 3:46:09 PM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

"Modelling must be performed with frequency higher than oscillator frequency, e.g. 100MHz step for 600-1000KHz oscillators."  - Buggins

This might be the most troublesome part.  FredM used to simulate his Theremin circuits and he said it would often take many hours or even days of simulation to make a few seconds of sound sample.  I suppose you only need a few cycles to put in a wavetable, but it can take a lot of simulation for the internal nodes to stabilize, and for the output to be valid.  And much of the sound is due to the speaker and speaker baffle, not the electronics so much.

I could be wrong, but I think in the end you'll be gluing together reduced alias DSP oscillators, filters, etc. to generate sound.  There isn't really just one Theremin sound, they're all over the map.  Even Clara's sound can vary quite a bit depending on the recording [LINK] [MP3].  I think players sometimes want a sine wave to do those "woo-woo" SF sounds of the Tannerin, but otherwise probably mostly want rich, resonant sounds that analog Theremins can at best only roughly approximate over limited pitch ranges, which are human vocals & the violin family (and the violin itself is something of a wooden human female vocal sim.).  I guess what I'm saying is IMO the analog Theremin isn't some ideal sonic target, it's just what we've all come to expect from a handful of parts rather cleverly strung together.  For as great a player as Clara was and for all she did to promote the Theremin, I personally I don't enjoy listening to the timbre of her Theremin for more than a song or two, it's too buzzy, and the low end oddly raspy.

When I first got my prototype working all I had was a sine wave, and I had fun playing that for a while.  When I got human vocals going I played almost nothing but female vocal sounds, which was a blast.  Now that I have violin and cello I find I'm not playing female vocals nearly as often.  It could be that I just get tired of the old sound and so the new one is better sounding to me by default, but I think most of it is vocals have more harmonics & resonances than a sine wave, and violins have more harmonics & resonances than vocals - there's just more going on in terms of organic acoustic cues.  Though the playing styles required to make vocal vs. violin sounds more realistic are different enough that many songs just sound better with one or the other.

And I think this is another failure of the Theremini.  They stuck a "woo-woo" synth sound engine in an instrument that should instead be making more organic noises (it needs more resonant filters).  I mean, so many Theremins have "VOX" in their name for a reason.  They just completely blew it on that design.

Anyway, please don't think that I'm trying to tell you what to do, or lecture you or anything. And there's often much to be discovered / learned when designers come at things from completely different angles.

Posted: 10/18/2018 6:59:25 PM
Buggins

From: Porto, Portugal

Joined: 3/16/2017


And I think this is another failure of the Theremini.  They stuck a "woo-woo" synth sound engine in an instrument that should instead be making more organic noises (it needs more resonant filters).  I mean, so many Theremins have "VOX" in their name for a reason.  They just completely blew it on that design.


The only preset I like in Theremini is Ethereal. IMHO it sounds nice.
The only useful feature of Theremini is reverb.

Posted: 6/20/2019 1:30:44 PM
Buggins

From: Porto, Portugal

Joined: 3/16/2017

I've returned back to Cora Z7 (Xilinx Zynq) based theremin implementation.

As a base, FPGA dev board Digilent Cora Z7 is used. For Audio I/O: two Digilent PMODs - I2S2 (24 bit 48KHz stereo line in/line out), AMP3 (24 bit 48KHz stereo codec / amplifier for headphones)
    

Polypropylene pipe based antenna mounts (like shown in Teensy Theremin thread), small 3D printed cabinet.

Designed schematics in KiCAD.

Routed and ordered PCBs:
    Theremin shield x1 (interfaces between Cora board and peripherals - LCD touch, audio boards, encoders board, pedals board, microSD)

    Oscillator boards x2 - NPN based as Dewster suggested, with 3.3V digital output of oscillator (60V on antenna, according to LTSpice simulation)

    Encoders board x1 (5 incremental encoders with buttons, + one tact button, connected via 16->1 MUX to save FPGA pins)
    Pedals board x1 (6 TRS for connection of 6 Expression (potentiometer based) pedals - 6 ADC inputs are used
    Audio connector board x1 (for mounting line in / out sockets, with soldered wires with audio jack on other end to connect to I2S2 pmod)
    PMod adapter boards x2 - for mounting I2S2 and AMP3 pmods on top of shield board

Waveshare 4.3inch capacitive touch LCD screen 800x480, 12 bit color used (4 bits per R, G, B) to save FPGA pins.

LCD controller IP will support hardware acceleration for realtime drawing of on-screen tuner (pitch preview).

Two PMod ports on Cora Z7 are left unused - for connecting of future H/W extensions.

Ethernet port may be routed to cabinet back, if needed.

I believe it's good hardware which is enough for implementing awesome digital theremin.

Posted: 6/20/2019 2:42:21 PM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

"I believe it's good hardware which is enough for implementing awesome digital theremin."  - Buggins

Yes, that looks like plenty of hardware to do just about anything!  You probably have enough memory even in the FPGA BRAM to do small reverbs and delays.  Too bad there isn't a bit more in the way of FPGA pins broken out to headers, there are probably gobs of unconnected pins, though I imagine the touchscreeen will give you lots of GUI I/O options.  I wonder how long that ARM9 embedded inside the FPGA takes to boot up into a useful state?  I suppose you could run it bare metal, but then I imagine you wouldn't have the benefit of a file system?

How will you be making electrical contact with the antennas?

Posted: 6/20/2019 3:10:15 PM
Buggins

From: Porto, Portugal

Joined: 3/16/2017


Yes, that looks like plenty of hardware to do just about anything!  You probably have enough memory even in the FPGA BRAM to do small reverbs and delays.


There are 512MB of SDRAM which is accessible via AXI. I'm already using it to read frame buffer data for LCD. It would be easy to add one more channel for audio I/O: reverb, some extra large WaveTable instrument data access, even DMA based (vs interrupt based) Audio IO from PS(ARM) side.


Too bad there isn't a bit more in the way of FPGA pins broken out to headers, there are probably gobs of unconnected pins, though I imagine the touchscreeen will give you lots of GUI I/O options. 


I've managed to fit most of connectivity into Arduino/ChipKit connector (+SPI connector).

Two on-board PMOD ports are left free - it's 16 free pins.
Anyway, there are 5 encoders, one button, 6 analog inputs (expression pedals with pots) and unlimited functionality of big enough touch screen.
I believe, up to 6 pedals may be used for controlling of effects, and/or for features like LOOP, recording, etc.


I wonder how long that ARM9 embedded inside the FPGA takes to boot up into a useful state?  I suppose you could run it bare metal, but then I imagine you wouldn't have the benefit of a file system?


AFAIK, FPGA loads its configuration from flash. But probably, PS (ARM) may load configuration from filesystem.
I didn't measure boot time. But I didn't see any delays in booting.
(E.g. power up -> test image on LCD was been displayed almost instantly - less than 1 second AFAIR). Let me recheck it.

Of course, I'm using bare metal mode (so far, only one ARM core). It's not hard to implement SDcard driver for bare metal (there are such drivers for FAT on SD even for Arduino). Since there is FPGA, it may implement hardware acceleration with DMA for reading/writing SD card blocks.
Of course, complex filesystems (e.g. NTFS) are too hard to implement. But FAT is pretty simple.

SD card may be used for storing instruments (configurations, wavetables, etc), accompaniments in MP3 or some other formats (ARM is enough for playback), for recording of theremin performance as audio file.


How will you be making electrical contact with the antennas?

There are two fittings for each antenna (ppl->metal) - one on antenna side, one on cabinet side. They should be connected with wire inside pipe.
Two 1/2" fittings used to mount cabinet will be used as antenna contacts.
As well it would be useful to pass grounding to middle fitting - mic stand (e.g. via volume antenna side mount) - it could improve antennas sensitivity (act mostly as a real grounding).

Posted: 6/20/2019 4:01:17 PM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

That boot time is really good!  I hate waiting for musical instruments to boot, every second waiting takes away 10% of my inspiration...

With 16 free pins you could easily do an AFE approach.  I could help you with that if you are ever interested in trying it.  High voltage! (Done dirt cheap!)

10 or so years ago I did a back of the envelope calculation on a common inexpensive FLASH memory IC and showed that it had enough storage and output BW to do a decent (i.e. better than anything else available at the time in HW, long samples, no looping, many layers) digital piano.  The BW would have required a little bit of RAM and a dedicated HW driver like an FPGA, which I suppose was one of the reasons why the approach wasn't on the market?  Then again, digital piano playback technology remains pretty awful to this day, which is a total crime/sin.  I've grown to loathe them (as well as fake pipe organs in churches).

Posted: 6/21/2019 3:19:23 AM
Buggins

From: Porto, Portugal

Joined: 3/16/2017

With 16 free pins you could easily do an AFE approach.


Analog front end?
Do you mean your PLL based front end?

Posted: 6/21/2019 3:35:15 AM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

"Analog front end?  Do you mean your PLL based front end?"  -- Buggins

Yes.  Precise quadrature alignment gives you the biggest voltage swing.  With high Q coils, even a small amount of phase error can really hurt resonance.  I believe the latest Xilinx SW supports System Verilog?  If you are interested in trying it out let me know.  My SV code is pretty polished at this point, but I've only targeted Altera devices with it - it may need some work to port it to Xilinx.  And I'm not sure what the internal FPGA facing ARM register set looks like.

Posted: 6/21/2019 7:58:05 AM
Buggins

From: Porto, Portugal

Joined: 3/16/2017


Yes.  Precise quadrature alignment gives you the biggest voltage swing.  With high Q coils, even a small amount of phase error can really hurt resonance.


Doesn't NPN based oscillator reacts on LC resonant frequency change (C change) faster than external PLL?
Frequency of oscillator output is being measured with very high precision using ISERDES/DDR components (e.g. it's equivalent of counter with 1600MHz rate, but there can be several such meters fed with OSC signal via slightly different delays - for 8 ISERDES meters it becomes 12.8GHz).
It gives a lot of bits. E.g. if time interval between OSC edges is 1us (1MHz), single measure will give count ~ 12800. Summarized for one 48KHz sample period (~20 measurements) it gives value near 266000 (18 bits). Since OSC frequency changes only in range 6-8%, 4 higher bits do not have a meaning. 
So, we have about 14 bits of oscillator frequency information collected during one sample.
Averaging will increase this value.

Does your PLL based method give more bits?


I believe the latest Xilinx SW supports System Verilog?  If you are interested in trying it out let me know.  My SV code is pretty polished at this point, but I've only targeted Altera devices with it - it may need some work to port it to Xilinx.

I'm sure .sv works ok in test benches but not sure about sysnthesis. Need to check. Do you have some sample .sv module which is easy to test?
So far, I'm writing in pure verilog.

Code:
And I'm not sure what the internal FPGA facing ARM register set looks like.

ARM connects FPGA peripherals via AXI bus (memory mapping).
Vivado has a wizard which can create stub implementing AXI slave with access to N registers which represent device. This code can be used as a base for implementing interface to your own module.

Each AXI device has its own address space - start address and some other parameters from device is being placed to .h file exported to SDK project with hardware definition.

So, in general, you just read/write memory using #defined address.
As well, your module may provide some better C interface (aka driver) - e.g. functions to get/set some registers or do more complex tasks. As well, internal registers layout may be represented as C struct. 

E.g., definition of my theremin sensor device:


Code:
typedef struct {
 /** 0: R/W  [4 bits] - pitch moving average filter window size log2 (0:1, 1:2, 10:1024) */
 volatile uint32_t pitch_moving_average_window;
 /** 4: R/W  [24 bits] - pitch scaler min value */
 volatile uint32_t pitch_scaler_min;
 /** 8: R/W  [24 bits] - pitch scaler range value */
 volatile uint32_t pitch_scaler_range;
 /** reserved, unused */
 volatile uint32_t reserved1[5];
 /** 20: R/W  [4 bits] - volume moving average filter window size log2 (0:1, 1:2, 10:1024) */
 volatile uint32_t volume_moving_average_window;
 /** 24: R/W  [24 bits] - volume scaler min value */
 volatile uint32_t volume_scaler_min;
 /** 28: R/W  [24 bits] - volume scaler range value */
 volatile uint32_t volume_scaler_range;
 /** reserved, unused */
 volatile uint32_t reserved2[5];

 /** 40: R/O  [24 bits] - pitch moving average filter output */
 volatile uint32_t pitch_avg_filter;
 /** 44: R/O  [24 bits] - pitch scaler output */
 volatile uint32_t pitch_scaler;
 /** 48: R/O  [24 bits] - pitch hand to antenna distance output */
 volatile uint32_t pitch_distance;
 /** 4C: R/O  [24 bits] - pitch note logscale output */
 volatile uint32_t pitch_note;
 /** 50: R/O  [24 bits] - pitch phase increment output */
 volatile uint32_t pitch_phase_inc;
 /** 54: R/O  [24 bits] - pitch distance delta (hand movement speed) output */
 volatile uint32_t pitch_dist_delta;
 /** reserved, unused */
 volatile uint32_t reserved3[10];

 /** 80: R/O  [24 bits] - volume moving average filter output */
 volatile uint32_t volume_avg_filter;
 /** 84: R/O  [24 bits] - volume scaler output */
 volatile uint32_t volume_scaler;
 /** 88: R/O  [24 bits] - volume hand to antenna distance output */
 volatile uint32_t volume_distance;
 /** 8C: R/O  [24 bits] - volume multiplier output */
 volatile uint32_t volume_mult;
 /** 90: R/O  [24 bits] - pitch distance delta (hand movement speed) output */
 volatile uint32_t volume_dist_delta;

 /** reserved, unused */
 volatile uint32_t reserved4[27];

 /** 100: R/O  [12 bits] - increments each sample */
 volatile uint32_t sample_counter;
 /** 104: R/O  [12 bits] - increments each 100MHz clock cycle, resets on new sample start */
 volatile uint32_t subsample_counter;

 /** reserved, unused */
 volatile uint32_t reserved5[4096-66];

 /** 4000 [512x24 bits] R/W pitch to distance lookup table */
 volatile uint32_t pitch_to_dist[512];
 /** 4800 [512x24 bits] R/W volume to distance lookup table */
 volatile uint32_t volume_to_dist[512];
 /** 5000 [1024x24 bits] R/W pitch hand distance to note lookup table */
 volatile uint32_t pitch_dist_to_note[1024];
 /** 6000 [1024x24 bits] R/W pitch note to phase increment lookup table */
 volatile uint32_t pitch_note_to_phase_inc[1024];
 /** 7000 [1024x24 bits] R/W volume distance to multiplier lookup table */
 volatile uint32_t volume_dist_to_mult[1024];
} ThereminSensorRegs;



You must be logged in to post a reply. Please log in or register for a new account.