Author: Scott Gravenhorst
Verilog, designed for Spartan-3E SK, certainly adaptable for others.
Digital Waveguide Experiment
This project is a digital waveguide experiment (physical model of a tight string). Access to the system is provided by an embedded MIDI controller.
The main components of the system are two 18 bit wide x 2048 location RAMs (block RAM) acting as digital delay lines, two simple numerically tunable IIR filters (identical to the filter used in the GateMan-I synth), a simple nonlinearity and a numerically moveable pickup.
The 2 RAMs are used to pass a signal from one end to the other with delay. Each RAM is dedicated to one direction, one left and one right. The pickup is a selectable tap point on each RAM which is summed to provide the system's output signal to the DAC.
Each RAM can connect through a nonlinearity at the input of it's IIR filter. The output of each filter is then routed to the input of the "other" RAM delay line. The delay lines are both clocked simultaneously at the sample rate of 500 KHz.
Additionally, each filter's output is inverted before applying it to the receiving delay line.
The nonlinearity consists of logic which monitors the delay line output amplitude and controls the gain applied to the signal passing through it. The threshholds of switching from unity gain to low gain are 3/4 of positive max amplitude and 3/4 of negative max amplitude. Between the threshholds, gain is unity. When the signal crosses the threshhold, the gain is reduced. This simple nonlinearity adds harmonic content and makes the sound more interesting, under some conditions it is rather bell-like.
Hard clipping is employed to prevent overflow distortion when the system is excited while it is already vibrating.
Excitation is applied through logic which sums the current output of IIR1 with the excitation signal. In this experiment, only a rectangular pulse is available, however, simple changes to the code can control the width of the pulse. Current code sets the pulse at a proportion of the waveguide delay line length.
The rotary encoder is programmed to control the IIR filters' bandwidth. The WEST pushbutton switch allows for much faster change of the filters' bandwidth value when the encoder is rotated. Both endpoint filters are controlled by the same bandwidth parameter and behave identically.
The EAST button is a squelch/damping control to silence the system. The SOUTH button controls the LED display and the NORTH button causes an MCU system reset. The right 3 slide switches select MIDI channel 1 through 8, the left most slide switch enables or disables the nonlinearity.
Here are some MP3 sample files, in each one, a single pitch was selected and the synth's reflection point filters were set at bandwidth = 0, then as the key is struck, the filter is opened up slowly to demonstrate the action of the filter. In all cases, most of the sample includes re-excitation of the system while it is already vibrating. Note how at first it sounds like clicks due to severe filter attenuation, then drum like and progresses to a more metallic sound.
- DWG_low.mp3 - low tone mp3 file
- DWG_mid.mp3 - mid tone mp3 file
- DWG_high.mp3 - high tone mp3 file
- DWG_proj_files.zip - Digital Waveguide Project Files
A Digital Waveguide Monosynth
- DWG_monosynth_ver_f.zip - A Simple Digital Waveguide MIDI Monosynth (ver_f)
Slide Switches: Switch 0 and 1 control the MIDI channel, 1 through 4.
Switch 2 controls the excitation pulse shape. If off or down, a velocity sensitive rectangular pulse height is used. If switch 2 is on or up, the pulse is a sawtooth, ramping front end with fixed height and width.
Switch 3 controls "linearization" of the filter. What I mean by "linearization" is that if the filter's bandwidth amount is the same for the entire range of pitches, the low end can sound like strings, but the high end sounds drumlike. To fix this, I invented 2 algorithms to help. With switch 3 in the up position, the system is better for strings. If switch 3 is in the down position, it will work nicely for drums. For strings, you want the filter bandwidth setting higher. For drums, you want the filter bandwidth setting lower.
Rotary Encoder: Controls the reflection point filter bandwidth. The higher the setting (twisting to the right increases), the more stringlike, and the less drumlike.
It is simpler and better to use the Karplus-Strong Single Delay Line model because it uses half the RAM to do the exact same thing.
The new code now plays 3.5 octaves, the modulation wheel sets the pickup position. The switches and rotary encoder are as they were in my first cut, above. This code uses 3 block RAMs instead of 4 for the string model (for a total of 4 including the MCU ROM instead of 5).
- DWG_monosynth_ver_g.zip - A Simple Digital Waveguide MIDI Monosynth (ver_g)
Some Observations and Conclusions:
In doing this project, I learned some important things. One of them has to do with delays. I initially expected that the waveguide's length would be the only factor in setting the pitch of a string. This is not true in this design because there is an IIR filter in the string loop. It turns out that the IIR filter provides a variable delay of zero to one sample time. Because the waveguides are 2048 locations long, this isn't a huge problem, but it is apparent when using two of these synths together. The more different the filter settings are, the more the strings are detuned with respect to each other. This can provide a nice phasing effect, so it's not entirely bad. Also, as the pitch is increased (waveguide length decreased), the effect of this extra delay is more and more apparent. I did not notice, however, that any pitch within the instrument's range was so far out of tune as to be obvious, only that as pitch increases, differences between filter settings make for faster phasing (for two or more synths played together).
Regarding the sound of the instrument, it is important to note that this physical model is one dimensional. That is, it simulates only one of the ways in which a real string vibrates. For more dimensions, additional waveguide systems are required per string. The FPGA I am using has too little internal RAM to do any more than 8 strings in one dimension (well, technically, I think I could squeeze 9 strings, but that would require a big change to the MIDI controller). Of course, other techniques might be able to better utilize the RAM and attain a higher string count.
The next version of this project may include SDRAM. The maximum number of waveguides will depend on how fast the design handles SDRAM transactions. The number of strings will be the max waveguide count divided by the number of dimensions supported. That said about dimensions, this instrument will not sound like any true natural instrument because of it's one dimensionality. It has characteristics that are found in natural instruments and I believe that is what gives this instrument a "more natural than a synthesizer" sort of sound. The instrument produces two major groups of sound, string-like and drum-like. With the filter bandwidth rather large, the sound will have more sustain and a brighter snappier attack, the lower register sounds harpsichord like and a bit higher sounds guitar-like. The more slowly decaying harmonics tend to have a metallic sound. The sound morphs slightly and gradually as you move up the scale. This effect is reduced by using more keyfollow for the pickup position. With low bandwidth, damping is rather quick, suppressing higher harmonics except during the very beginning of the sound. The drum sounds can be made to sound very much like tonal bongos or tonal congas, lowering the bandwidth increases the damping while increased bandwidth gives a longer resonant decay.
BassDaWG/4 is a modified version of PolyDaWG/8. It has 4 strings that are twice as long. Since the sample rates are the same, this instrument is an octave lower. It also has an additional octave of range (4.5 octaves). The filter bandwidth algorithms are modified for better consistency of bass sound across the keyboard. With proper filter and pickup position settings, it can be made to sound something like a fretless electric bass though not exactly. BassDaWG/4 is also polyphonic, so you can do common bass chords and accent harmonies.
Digital techniques used to provide waveguides create design boundaries that must be considered. Pitch resolution of a waveguide depends upon the number of stages in the delay line. The more stages, the smaller will be the granularity of pitch. (Note: a method called "fractional delay" is available to allow more pitch resolution with a small computational sacrifice. Lagrange interpolators are an example of this). However, each doubling of the length of a waveguide causes the pitch to drop one octave assuming that the sample rate doesn't change. If one wishes to compensate for the lower pitch, one is required to increase the sample rate. So while it's desirable to have long waveguides for pitch accuracy, long waveguides also demand higher sample rates to maintain pitch range. Higher sample rates then restrict how much work the controlling state machine can do in each sample time. This leads to design restrictions such as the number of string objects provided or the number of features provided. The PolyDaWG/8 synth was designed with a "happy medium" approach. Initially, PolyDaWG's designed waveguide lengths were determined only by the amount of RAM available and the sample rate of between 125KHz and 250KHz was chosen to provide sounds in the range of normal human hearing. The waveguide lengths chosen provide only about 3.5 octaves because the pitch resolution becomes too large at the high end of the pitch range. In designing PolyDaWG/8, I used a C program which calculates the correct waveguilde length of each note of the scale (starting at some reasonable low value) for a given sample rate. The program stops as soon as it encounters a waveguide length which produces a pitch that is more than 5 cents out of tune. 5 cents is considered to be undetectable as "out of tune" by most humans. This data was then used to generate the tuning ROM values.
Phase 3: PolyDaWG/8
8 Voice Polyphonic Physical Model String and Drum Synth
Designed by Scott R. Gravenhorst, 24-Aug-2007
An eight voice polyphonic MIDI synthesizer based on the Karplus-Strong single delay line with movable pickup string model. This design uses only the FPGA internal resources and an extern SPI 12 bit DAC. 17 block RAMs and one multiplier are used. This is the first step in creating a larger polyphonic harp. The next step will be to replace the block RAMs with SDRAM, after which the block RAMs are available for additional features. The RAMs for the comb filter are created using distributed RAM instead of block RAM.
Switch [1:0] select MIDI channel 1 through 4
Switch 2 controls the excitation pulse shape:
off = velocity controlled rectangular pulse on = fixed width sawtooth pluck
Switch 3 Controls filter keyboard follow setting
off = better for drums, set bandwidth low on = better for strings, set bandwidth high
The LEDs display the upper 8 bits of the filter rotary encoder value which is a basic indication of the filter setting (because this value is then modified by the note number received on each string pluck, see Verilog code).
There are two delay lines per string, the string loop delay line is 2048 x 18 (two block RAMs). The comb filter delay line is 256 x 18 (distributed RAM, four 64 x 18 RAMs).
The single shared reflection point filter (Variable IIR) is controlled using the rotary encoder. Clockwise rotation increases the filter's bandwidth. Higher bandwidth will give a more metallic sound with longer sustain. Lowering the bandwidth shortens sustain. If the bandwidth is low enough, you will get drum-like sounds (kind of like tonal bongos or congas). For drum sounds, switch 3 should be set off so that the entire range becomes drum sounds. For plucked strings, set switch 3 on for consistent string sounds accross the instrument's range. As a side note, my interest in digital waveguide synthesis began when I read about a college FPGA project called Terror Mouse, a 6 string "guitar" model implemented in a small FPGA. The Terror Mouse design used a shelving filter which at the time I didn't completely understand. This fact led to my choice of a simple single pole lowpass IIR filter. Recently (May 1, 2011), I revisited the PolyDaWG-8 synth and added a selectable reflection filter module. Two filters were implemented, the simple single pole filter that I had already been using and the Terror Mouse shelving filter (using the designer's coefficients). In a word, I'd describe the shelving filter as underwhelming. It works, but does not emulate an "acoustic guitar" as was the designer's intention. In fact, the single pole lowpass IIR is tunable and versatile enough that it can provide a timbre so similar to the Terror Mouse shelving filter as to be indistinguishable. All in all, I prefer the single pole IIR design because it can be tuned so easily. The coefficients are calculated on the fly in the FPGA.
The pickup position is controlled with the MIDI modulation wheel. It affects the sound much like an electric guitar's pickup position. Note that because of dirty RAM after moving the wheel, the synth may produce a low level of noise while moving the wheel. The noise abates as soon as the wheel stops moving. If I find a way to fix this, I will (future version). For now, the mod wheel is meant as an easy way to set the pickup position without system exclusive messages. In fact, there are no system exclusive message settings at all in this version. It is probably not good to use the modulation wheel as a performance control in this version. The Pitch Wheel is not programmed nor is after touch.
The instrument's timbre is controlled by both the variable IIR reflection point filter and by the pickup position. The reflection point filter determines how quickly the string is frequency and amplitude damped while the pickup position sets the intrument's "mellowness" factor. With the mod wheel at low values, it is like an electric guitar's bridge pickup while higher values give a sound like a neck pickup. The neck pickup settings tend to have fewer high harmonics whereas the bridge pickup settings tend to have more.
The sustain pedal acts like a piano's. Undepressed, each note played is held only as long as the key is held down. Depressed, each note played rings until the pedal is released. Note that this synth is only 8 voice polyphonic, so if sustain is held down and more than 8 notes are played, an older ringing string will be "stolen" to produce the newest note's sound.
I have found that two of these instruments on the same MIDI channel with slightly different filter settings sound very full and rich. his is due to the fact that the filter provides a small delay which varies with bandwidth. Thus different filter settings will change not only the timbre of the string, but also the pitch. The pitch is affected by this more at the low (especially drum sounds) end of the filter's setting range. The pitch difference is then responsible for a phasing effect, something like that heard from a piano (though this synth is NOT a piano model).
The basic pitch range of the instrument is determined by the sample rate. As it exists in the zip file, the system is programmed for 250 KHz. You can change this to 125 KHz to lower the instrument one octave. The range is 3.5 octaves regardless of the sample rate.
The nonlinearity I experimented with in the monosynth version has been removed. I've determined that if there is an effect from it, I cannot hear it.
Controlling the pickup position:
There are two modes for controlling the pickup position, Normal and Original.
The machine powers on in Normal mode.
You can look at the lower right corner of the LCD to determine the mode. N for normal, O for original. A bit cheesey, I know, but it works.
The mod wheel selects 1 of 8 keyboard follow configurations. SETTING CHARACTER 0 very treble, low moderate keyboard follow 1 treble, moderate keyboard follow 2 full keyboard follow 3 bass, moderate keyboard follow 4 bass, low moderate keyboard follow 5 bass, low keyboard follow 6 very bass, slight keyboard follow 7 bassiest, very slight keyboard follow The current setting will be displayed in the LEDs while button SOUTH is depressed. Otherwise, the LEDs display the rotary encoder value.
This is the first control mechanism I designed and it has it's uses. In this mode, the pickup position is not adjusted according to the note played. Instead, the position value is controlled by the modulation wheel and it's value is applied to all notes regardless of the length of the string loop waveguide.
I tightened the string processing state machine from 14 clocks per string to 6 clocks per string. The total is now 49 clocks for the entire harp (one clock to initialize and start the state machine plus 6 clocks per string). This version is capable of a sample rate of 1 MHz.
Other code cleanup to reduce the number of warnings and to conserve FPGA resources where possible.
Correct anomoly in voice assignment.
ver_n is tunable, it seems usable, but I am not happy with the granularity. Tuning was accomplished by changing the SPI DAC controller to allow inserting a variable number of extra clocks between DAC enable pulses which lowers the sample rate. This design uses RAM wherever possible instead of flipflops. Using RAMs requires more state machine steps for each string increasing the total time for computing the harp to 57 clocks. The filter now uses 3 clocks instead of 4, this helped mitigate the longer time required by the RAM method. ver_n was tested using WebPACK ISE 9.2i.
- Removed several states from the main state machine. Max sample rate for this version: 862068.966 Hz.
- This version allows tuning via rotary encoder to change the sample rate downward. At startup, the lowest C key should be a G# (before the rotary encoder is moved). Therefore, it will require you to tune it as you need it.
- New range is 57 notes.
- Add linear attenuation (uses JOY_X and JOY_Y).
It was simple to convert PolyDaWG/8 into a 4 string polyphonic bass version. It works the same, but has strings twice the length of PolyDaWG/8. BassDaWG/4 has a 4.5 octave range.
The same voice assignment anomoly as was found in PolyDaWG/8 was corrected in BassDaWG/4 ver_b.
And here is the current code:
- PolyDaWG6.zip - PolyDaWG/6 Source and Documentation files (ver_f, 6 voices) - OBSOLETE
- PolyDaWG8.zip - PolyDaWG/8 Source and Documentation files (ver_n, 8 voices) Updated 11-17-2007
- BassDaWG.zip - BassDaWG/4 Source and Documentation files (ver_c, 4 voices) Updated 09-09-2007
- PolyDaWG8_ver_oa.zip - PolyDaWG/8 ported to run on the small Avnet Spartan-3A board
- PolyDaWG_8_S3Esk_ver_q.zip - PolyDaWG/8, Spartan-3E Starter Kit. Source code and docs. Updated 11-28-2010.
- PolyDaWG6-1.mp3 - PolyDaWG/6 Small Example Sound File (strings)
- PolyDaWG8-1.mp3 - PolyDaWG/8 Small (90 seconds) musical piece demonstrating the synth. I used 2 instruments, one is panned hard right, the other is panned hard left. The stereo and phasing effects of the two together is nice, but if you want to hear each instrument alone, you can. The difference between the two instruments is the filter setting. This piece uses a constant value for the pickup position.
- PolyDaWG8-2.mp3 - PolyDaWG/8 ver_j, content similar to above, but the pickup position follows the keyboard, shortening the distance between the bridge and the pickup for higher notes.
Note that this is a work in progress. As time goes by, I will probably find things to improve. I will post messages to SDIY and FPGA-Synth to inform of any changes to the code.
The MIDI controller contains all the basic MIDI performance stuff plus code to perform system exclusive based updates to the hardware's operational registers or RAM (Note that this feature was not used in the DaWG synths). The code also includes support for the LCD display on the S-3Esk. The current code is 0x20B locations in length, so there is still plenty of room for features or whatever.
Thanks to the members of the FPGA-Synth list for all of the help I have received there.
Rotary Encoder Odd Behavior
The rotary encoder on the Spartan-3E Start Kit can be either a clockwise or a counter clockwise unit. If the filter action is backwards, you can force it to work correctly by placing a 10K resistor across user jack J1 pins I01(B4) and either Vcc or Gnd. The ones with white plastic bodies seem to work backward. For the blue body rotary encoders, put the resistor to Vcc, for the white body units, put the resistor to ground. This will correct the problem only using this synth (and future synths I design based on this board).