Let's Design and Build a (mostly) Digital Theremin!

Posted: 5/16/2019 2:36:05 AM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

2nd Order Bass & Treble EQ

It takes more real-time, and a bit more code, but second order is definitely the way to go for bass and treble EQ, even if the goal is a first order roll-off:

As seen above, even with both controls maxed out there is relatively little midrange interaction with second order filters.  The real-time and code spent linearizing the control for the first order case can be spent on the extra filtering of the second order case, as two first order filters in a row tuned the same way don't need any special treatment in that department.  Here's the updated spreadsheet: [LINK].

[EDIT] I was able to refactor the gray filter, so that it and the bass and treble tone controls now employ the same base subroutines.  I'm getting near the end of available processor memory (>93% utilized), so it's a lucky thing that the SW side of this feels like it's near to wrapping up.

[EDIT2] These kinds of EQ controls on something like a home stereo can be rather mild, so you could probably get away with first order filters there.  On a synth they're more of an effect, and second order gives you more "oomph".

Posted: 5/17/2019 5:35:09 PM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

Two Knob Preset Load / Store Considered Harmful

Up until now the D-Lev prototype UI (user interface) has employed two knobs for loading and storing presets.  Preset slot 0 was for system, or global, presets, while slots [1:127] were for user presets.  To copy one slot to the other was as simple as setting the "Load" and "Stor" knobs to the desired slots and initiating a write (by pressing the auto-calibrate knob).  To save only the system parameters, one would set the "Stor" knob to 0 and do a write.  This would also set the power-on slot to whatever slot the "Load" knob was set to when saving the system parameters.

Roger prompted me to consider implementing a two step store to make things safer, and a short while ago I did so.  A couple of days ago I accidentally overwrote my violin preset (waa!) because I forgot to set the Stor knob correctly during a write.  This made me feel that the two step store was perhaps not all that fundamentally safe with two knobs, and that the two knobs were the root of the problem.  There's too much going on during a write for my poor brain to pay full attention to all the important details.

After some thought I've implemented the following:

There is only a single "Load" knob now, and it functions thusly:
- Preset store also stores system parameters, there is no special system slot visible to the user.
- System parameters load only at power-up.
- User slots are [1:127].
- The slot that was last written to is the power-up default.

So the way to, say, copy slot 3 to slot 7:
1. Set "Load" knob to slot 3.
2. Press "C/wr".  "?WR?" appears on the LCD and an LED lights.
3. Set "Load" knob to slot 7.
4. Press "C/wr".  The write happens and "?WR?" goes away and the LED extinguishes.

Doing anything other than twiddling or pressing "Load" while "?WR?" is on the screen blows out of the write.  If you changed "Load" to a different slot before the blow-out, that slot will load after the blow-out.

During the write arm, I can see the FPGA board LED light up through the translucent enclosure, and think it is a good addition as it draws your attention to something very important going on.  On a production model this would be a panel LED.

The final implementation will likely have negative ROM preset slots (once I work out a way to save and restore presets via the serial port - probably via TTL script).  The plan is to have slot -128 be the invisible system preset, slots [-127:-1] be ROM "factory" presets, and slots [0:127] be R/W user presets.  A "factory reset" would then overwrite the user presets with the factory presets.  I'm on the fence regarding preset 0, not sure whether to force it to be the power-on default, or keep the last slot written as the power-on default.  The last slot written method is sort of a superset, as one could always write the desired default voice to slot 0.

Since this code is executed on thread 7 it was kind of a bear to get it working correctly.  And the logic paths are surprisingly deep and broad.

Getting rid of the "Stor" knob on the main page opened up some prime real estate for development, and I stuck the global volume there.

Posted: 5/19/2019 5:56:49 PM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

Everyone Loves Raymond Presets!

Tera Term is an open source terminal emulator that I've been using to communicate with my Hive processor on the D-Lev prototype.  Tera Term has a TTL macro language that is feature rich enough to easily automate many tasks and, coupled with my HCL command line interface, I'm able to upload code to the EEPROM, peek and poke memory locations and registers, etc. 

The SPI commands I implemented in HCL allow me to read and write the EEPROM directly, and since the presets are stored in there (along with the SW image), one should be able to manage presets off-line.  Indeed, this is what I just accomplished.  Took maybe a day total, and a lot of that was coming up to speed on various TTL commands that I've never used.

I wrote a TTL macro that reads the info contained in a specified D-Lev preset slot and writes it to a specified file.  And I wrote another that reads data from a specified file and writes it to a specified the preset slot on the D-Lev.  TTL has a "filenamebox" command that conveniently lists the files in a directory, which came in quite handy.  And I put in a global loop to automate multiple slot operations.

Works like a champ!  I should have done this ages ago, it would have made tinkering with the preset code much less nerve wracking.  Now users (currently just Roger and me) can exchange presets!

The macros accept as valid input the preset slot range of [-128:127].  This signed range maps to the unsigned EEPROM address range in the usual modulo fashion:
  preset slot [0:127] => EEPROM addr slot [0:127]
  preset slot [-128:-1] => EEPROM addr slot [128:255]

The preset knob on the D-Lev will be changed from [1:127] to [-127:127], with slot -128 (128 unsigned) being the (inaccessible directly) system parameter slot.  The negative presets will be read-only via the D-Lev parameter management system, but will obviously be readable & writeable via the TTL macros.  This, along with a simple factory reset, will be, AFAICT, among the last major SW edits / additions.

[EDIT] Just finished the D-Lev SW edits to enable the preset range [-127:127], uploaded the system params to slot -128, and BAM! factory presets are a thing.  Now to code a factory reset...

Posted: 5/20/2019 11:51:37 AM
pitts8rh

From: Minnesota USA

Joined: 11/27/2015


Yesterday I spent a little time (very little) uploading and looking at the 2019-05-17d software on my prototype, after sitting on the last six or so updates while busy doing other stuff.  I went immediately to this version but decided it was too much of a shock so I went back and uploaded each incremental good version to ease into the changes.  For some reason I was fighting pitch jitter right from the start, but after backing up to the older SW that I have been using most recently I realized that it is a grounding/environmental noise problem, and that it was not related to anything new.  When I get back on it I'm going to continue with the theremin ground-current/noise-injection tests that were showing some interesting results.

I've only tried a few things so far, but here are some impressions. 

1)  The double-press store procedure seems to be a win, if I understand it correctly. I gave it the "intuitive" test by not reviewing your procedure first, instead just going for it, and it worked as expected.  Going through the preset touch-up to fix my "vmod" and other parameter changes was less nerve wracking when I realized that I didn't have to be cautious when pressing the encoder button.  I was hoping for a flashing LED but I'm going to have yellow "STORE" led below my display, and even a solid light should do the trick.

2)  P/V reverse (to swap the pitch and volume sides) caught me off guard until I remembered that I had read something about it in your change log.  It seems like such a simple thing now, but this could be a huge boon to lefties who have had to beg for custom builds or live with the backside control reach-around.  This could be an argument for keeping the AFEs and inductors in the extension arms or on/near the antennas, with a common plug-in or screw-on connection at the enclosure.  From a manufacturing standpoint, a left or right product could be identical.

3)  I was a little disturbed at the prospect of losing the process of requiring SYSTEM parameters to be intentionally stored in preset 0 and instead making it automatic anytime there is a user preset store.  I sometimes make system changes that I don't necessarily want to keep without paying much attention to the baseline values so that I could get back to the original settings should I accidentally overwrite them.  Being already familiar with most of the D-Lev interface makes this a minor detail to deal with, but to someone starting out it may eliminate the safe wall between the global system parameters (some of which may be painful to lose) and the user preset "play area".

But again, this perception is based on limited experience with the new software, so you may have good reasons to have made this change.

4)  I have not tried the read and write macros yet, but I will today.  I don't think I have many worthwhile presets that you don't already have, but maybe we can pool them anyway.  I've avoided spending much time building presets because of the frequent changes, but it sounds like the time has come.

5) Same goes with the new 3-oscillator source.  I haven't explored it, but more synthesizer capability can only be good.


We should be seeing some progress on the boards early next week.  The software should never be completed before the hardware (ha, as if software is ever finished)!

Roger

Posted: 5/20/2019 2:57:49 PM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

"I was hoping for a flashing LED but I'm going to have yellow "STORE" led below my display, and even a solid light should do the trick."  - pitts8rh

Getting the new load / store logic working was rather involved, mostly because it was in crying need of refactoring since the change from the LCD being regularly refreshed to an on-demand type of thing (to minimize interference with the axes).  The edits didn't boil down to all that much, but the boiling required a lot of basic orthogonal steps involving several factors.  And, this being thread 7, the simulation had to happen largely in my head, so bugs tended to live unsolved for long periods of time.  One bug I hit was the 6ms page write time of the EEPROM, couldn't figure out why the system parameters were getting written but the user preset parameters weren't.

I could probably make the LED blink by ANDing it with a bit from the Hive TIME register.  Will give that a shot.

"2)  P/V reverse (to swap the pitch and volume sides) caught me off guard until I remembered that I had read something about it in your change log.  It seems like such a simple thing now, but this could be a huge boon to lefties that have had to beg for custom builds or live with the backside control reach-around.  This could be an argument for keeping the AFEs and inductors in the extension arms or on/near the antennas, with a common plug-in or screw-on connection at the enclosure.  From a manufacturing standpoint, a left or right product would could be identical."

Since the switch happens right at the HW register level, it does seems like an argument to keep the left and right physical axes as similar as possible (ala my prototype), or physically swappable.  I don't think the AFEs and coils would need swapping, just the antennas themselves?  One argument against swapping coils is the FPGA DPLL parameters are (roughly) optimized for a given coil L (combined with the antenna C).

And, yes, huge boon to lefties (or anyone wanting to play reversed) and to the manufacturing side.  It was like a 5 minute edit!

"...backside control reach-around."

Hey now, this is a family forum! ;-)

"3)  I was a little disturbed at the prospect of losing the process of requiring SYSTEM parameters to be intentionally stored in preset 0 and instead making it automatic anytime there is a user preset store.  I sometimes make system changes that I don't necessarily want to keep without paying much attention to the baseline values so that I could get back to the original settings should I accidentally overwrite them.  Being already familiar with most of the D-Lev interface makes this a minor detail to deal with, but to someone starting out it may eliminate the safe wall between the global system parameters (some of which may be painful to lose) and the user preset "play area".

But again, this perception is based on limited experience with the new software, so you may have good reasons to have made this change."

Now that there is only one preset knob, it's not as simple to bifurcate the storing of settings.  Or at least I haven't been able to think of an obvious way to do so.  I do agree that the system parameters are painful to lose, as the whole feel changes subtly and uncomfortably when I mess with them, and to have them updating on every store feels a bit dangerous.  Slot 0 is still something of an odd man out, as there is no equivalent factory preset slot.  I could easily make it so that writing to slot 0 is the only way to save system parameters (along with the preset parameters)?  I currently have all writes (both system and user parameters) disabled for the factory slots (you can only program the user parameters via external means).

"4)  I have not tried the read and write macros yet, but I will today.  I don't think I have many worthwhile presets that you don't already have, but maybe we can pool them anyway.  I've avoided spending much time building presets because of the frequent changes, but it sounds like the time has come."

I've had so many presets wiped out due to SW updates that I've gotten out of the swing of working on new ones.  The macros are really encouraging from this standpoint.  The synth could possibly change somewhat, and it is now possible to rearrange the data in the captured presets to follow those changes.  I may do this anyway, as the preset area in memory is a bit jumbly, and the type system could use a bit of cleaning as well.

[EDIT] Just made those SW changes, where writing to preset slot 0 is the only way to save system parameters.  This has the side effect of making slot 0 the power-on default, which is fine with me.  Also implemented the blinking write LED.

Posted: 5/20/2019 6:04:46 PM
pitts8rh

From: Minnesota USA

Joined: 11/27/2015

"One argument against swapping coils is the FPGA DPLL parameters are (roughly) optimized for a given coil L (combined with the antenna C)."

I guess I was thinking of the situation with wide-capture (or whatever you would call it) DPLL parameters that I currently have that can accept a wide inductance range.  I forgot that you planned to go back to narrower, inductor-optimized parameters once things settled out.  Does this mean that you would have two different inductances, each with optimized DPLL settings, and then essentially swap the oscillators via software?  IOW, a right handed theremin with a pitch oscillator frequency higher than the volume side converts to a left handed version with volume freq higher than pitch freq?

"[EDIT] Just made those SW changes, where writing to preset slot 0 is the only way to save system parameters.  This has the side effect of making slot 0 the power-on default, which is fine with me.  Also implemented the blinking write LED."

Awesome!  You are too easy.

Posted: 5/20/2019 8:59:47 PM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

"Does this mean that you would have two different inductances, each with optimized DPLL settings, and then essentially swap the oscillators via software?  IOW, a right handed theremin with a pitch oscillator frequency higher than the volume side converts to a left handed version with volume freq higher than pitch freq?"  - pitts8rh

Yes, exactly.  If you don't optimize the FPGA LC_DPLL code for the LC resonant frequency, then the loop bandwidth varies inversely with actual LC resonance. For instance, optimizing for 1.23 MHz LC resonance gives a loop BW of 148 Hz.  Running this at 1.0 MHz LC resonance gives a loop BW of 183 Hz.  It's not a linear relationship, but rather more complex to calculate (the NCO gain depends on the operating point) so I let the FPGA compiler do these calculations for me as part of the build process.  Though loop BW is a first order LPF, so it doesn't influence the gestural BW all that that profoundly.  LC_DPLL optimization is a power of 2 kind of thing (performed with fixed shifts) so it's pretty rough, but the final design should probably be optimized just to rule out any possible extra weirdness.

I want to thank you again Roger for suggesting the encoder push => zero out the parameter feature.  With it I can whip on through the screens and quickly null everything that isn't needed for the desired preset.  And the current preset load / store scheme you suggested seems generally superior to what I had previously.  Having the system parameters only saving at slot 0 seems like an improvement too, as it gives two reasons (safer sys write, power-up default voice) for the otherwise odd man out nature of slot 0.  Two heads are better than one, particularly when the added head is yours!

Posted: 5/25/2019 12:19:10 PM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

Formants & Filtering & Coherence

How many formants do you need to cover most of the organic synthesis bases (without getting into crazy territory like ride / crash cymbals)?  The D-Lev once had 6 pitch and volume modulated formants.  I changed this a while back to 3 modulated and and 9 static formants for a total of 12, but I've had this feeling that these (ratio and total) could benefit from more thought.  Human vocals need ~4 static formants, and dynamic vowels only require one or two of these to be modulated.  My "eerie" preset used six modulated formants and no static formants, but one could argue that it's more of a "woo-woo" than organic preset.  

I've been using a six (static) formant cello for a while now which is augmented on the top end by an inharmonic resonator, and thought at one point that it would benefit from a larger quantity of explicit formants, but this hasn't been the case.  Making a cello with formants alone clearly doesn't yield as good a quality voice as the formant / resonator combination, and I've long wondered why that was.  The way I "tune" this formant / resonator combo is by observing white noise passed through them on the spectrum analyzer in Adobe Audition.  I set the bottom formant to the known lowest instrument resonance, with the rest spaced roughly exponentially up to 1kHz or so, and then I play with the resonator high pass filter and other settings to try to blend the transition of the two groups of resonant peaks.  There is an obvious phase difference that produces a dip or null at the transition, and in the past I've endeavored to minimize this, but have since come to realize that this "accidental" null is an essential component of the cello sound.  Indeed, string filters often have an additional global shaping filter which contains a deep notch.

On the D-Lev there are many enables and modes associated with the formants and resonator, many of which switch the audio off entirely, requiring one to sometimes go on a "knob hunt" to find the source of the constipation.  Now that the "push to zero out" feature has been implemented for all encoders, many of these switching features aren't as useful nor necessary, so I've been able to considerably simplify them.  I removed an ~80 cycle knob that sequentially enabled the formants, and replaced it with a 16 cycle ORing of all formant knobs, which produces a testable flag that, if false, bypasses the formant bank.  And now zeroing the resonator knobs puts it in parallel mode (in parallel with the formants) and kills the resonator output, which removes it from the signal path without killing the signal.  I also added a 180 degree phase inversion mode to the resonator, to help it blend better with the formants (should one desire that for non-cello voices).

The removal of one crummy knob, the formant enable, has opened up a world of new UI possibilities for the formant section.  I decided to do 4 mod formants and 4 static formants, with 1 mod and 1 fixed per UI page, and all pages identical (except for the page identifier text on the LCD).  This makes the resonance knob common to 2 formants instead of 3, which is finer grained.  And it lops one page off the UI, which is always good.  I feel pretty OK with 8 formants total, and with the 50/50 mix of mod and static.  Things you can't know up-front in a design...

Implementing the bass and treble controls made me wonder if I could do parametric boost / cut with the oscillator and noise filters.  There was room for an additional knob (!), and it looked good in simulation.  But in use, half of the modes feel redundant (boost low-pass ~= cut hi-pass, etc.), and cut associated with bandpass doesn't function well at higher resonance settings (the cut reverses and turns back into a boost).  So keeping the current selector knob (-4=notch2, -3=lp2, -2=bp2, -1=hp2, 0=thru, 1=hp4, 2=bp4, 3=lp4, 4=notch4) and implementing just the boost side (which is a simple 5 cycle cross-fade of the I/O of each 2nd order section) adds versatility to filter shaping without significantly increasing confusion.

I've probably said this before, but you need 2nd order filtering before anything interesting starts to happen, and having 4th order on tap is quite powerful. There are clear use cases for both 2nd and 4th order filtering, but I don't believe a 3rd order option would be all that useful.  Things no one tells you...

Finally, I re-examined the transfer of DSP data between threads and took steps to ensure this was being done in a phase / delay coherent manner.  You basically sequentially read all inputs at the start of processing and then sequentially write them at the end.  With interleaved thread interrupts and dynamic processing, the transfer could possibly bobble by one sample and cause glitching, but all inputs or outputs would bobble together as long as they are done in the same order and with the same timing on both ends - and this is a different problem than coherence.

[EDIT] Trying to get my eerie patch back: [MP3]

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