262 Analog emulation monosynth

262 : Analog emulation monosynth

  • Author: Toivo Henningsson
  • Description: One synth voice with two oscillators and a 2nd order filter
  • GitHub repository
  • Clock: 50000000 Hz

How it works

The synth contains two oscillators with controllable frequency and waveform and a second order low pass filter with controllable cutoff frequency, resonance, and input amplification, similar to a simple analog synth. (Though the analog synth usually has the variable amplification after the filter.) The created audio samples are passed through a pulse width modulator (PWM) to create an audio signal on an output pin.

Sweep rates can be programmed for each parameter, to create simple envelopes. All parameters can be set through a register interface. By changing the sweep rates at specific points in time, more complex envelopes can be created.

The sample rate is 50 MHz/32, or 1.5625 MHz, far above the audible range, to avoid aliasing issues while allowing a fine enough spacing of oscillator frequencies: the oscillator period is always a whole number of samples, which avoid inharmonic aliasing effects.

The oscillators use counters that count down by a 2^n each sample. If the counter would become negative, the period is added. A second (n bit) sawtooth counter counts how many times the period has been added. After one period, the sawtooth counter has incremented 2^n times, and wraps around.

To reach lower octaves, an octave divider is used. oct_enables[i] is high once every 2^i cycles. An octave oct is specified for the oscillator frequency, and the counter is only update when oct_enables[oct] is high. This keeps the same relative frequency accuracy for each octave.

The octave + period arrangement means that the full period is specified in a simple floating point format. This serves as a quasi exponential conversion, which emulates the V/octave, V/dB etc scales typically used in analog synths, and causes a quasi exponential response when sweeping the frequencies.

The filter is two pole filter with two states. Small update steps are taken every sample. Instead of a multiplier, a barrel shifter (variable right shift) is used to calculate the state change. The barrel shifter and associated adder is shared between all filter update steps (and the dither step for the PWM). The synth cycles through the 6 steps for each sample, and adds steps up to 32 to come up to 5 bits of PWM resolution. The PWM resolution is increased through dithering; at 48 kHz sample rate, it can be considered to be 10 bits. The resolution should further increase for lower frequencies.

The octave of the cutoff frequency is used to determine the shift amount. Depending on the position within the octave, the shift amount is decreased by one more or less often, to average the right amplification.

The volume is adjusted by tying the filter update that feeds the input signal into the filter to its own frequency. In the same way, the damping as adjusted by having a separate frequency for the filter's damping step.

The a dither signal is formed by bit reversing the oct_counter counter (which is used for the octave divider), and added to the output signal before rounding off to 5 bits for the PWM output.

For more details, see README.md in the project repository.

How to test

The synth is controlled by writing to its configuration registers:

  • Keep the write strobe low when not writing.
  • Set the 4 bit write address, and an 8 bit data value.
  • While keeping the address and data stable, bring the write strobe high and then low again.
    • The write address and data are sampled at 2-10 cycles after the rising edge of the write strobe.

The output comes in two forms:

  • As a Pulse Width Modulated (PWM) signal.
  • As an 8 bit value on the 8 output pins, that can be reconstructed using a resistor ladder.

The PWM signal should be simpler to use, but be sure to reduce the voltage with a resistive divider or similar before connecting it to an audio device. Note:Make sure that you know what you are doing when connecting an audio device to the output. Don't apply more than 1 V between the terminals of an audio plug that is connected to line in or similar. 3.3 V direct from the chip might damage your audio device.

Most control registers consume 16 bits of address space each. The memory map is laid out as follows: (one 16 bit word per line)

offset |  high byte |     low byte |
-------|------------|--------------|
 0     |        osc1_period        |
 2     |        osc2_period        |
 4     |      cutoff_period        |
 6     |        damp_period        |
 8     |         vol_period        |
10     | osc2_sweep |   osc1_sweep |
12     | damp_sweep | cutoff_sweep |
14     |        cfg |    vol_sweep |

The registers are initialized to all ones at reset, which turns off all oscillators. The frequency registers are in a kind of floating point format:

  • Oscillator periods are 13 bits: 4 bits exponent + 9 bits mantissa
  • Cutoff, damping, and volume periods are 9 bits: 4 bits octave + 5 bits period
  • Sweep periods are 8 bits signed: 1 bit sign + 4 bits octave + 3 bits mantissa

Increasing the exponent by one doubles the period, and goes down one octave. An exponent of 15 turns off the oscillator. The volume depends on the ratio between the cutoff and volume periods (not their float representations). The damping depends on the ratio between the cutoff and damping periods (not their float representations). As the damping period gets longer than the cutoff period, resonance increases around the cutoff frequency. If damping is low and/or volume is high, the filter will begin to saturate (which is sometimes a desirable effect).

Each sweep will increase or decrease the corresponding period.

The cfg register contains additional settings:

  • Bits 0-1: Waveform for oscillator 0: 0 = pulse, 1 = square, 2 = noise, 3 = saw
  • Bits 2-3: Waveform for oscillator 1
  • Bits 4-5: Unused
  • Bits 6-7: Filter mode for oscillator 1 and 2 respectively, 0 = 1st order falloff, 1 = 2nd order falloff

For more details, see README.md in the project repository.

External hardware

audio plug to connect to audio input, voltage divider to protect it!

Picture

IO

#InputOutputBidirectional
0write data bit 0sample bit 0write address bit 0
1write data bit 1sample bit 1write address bit 1
2write data bit 2sample bit 2write address bit 2
3write data bit 3sample bit 3write address bit 3
4write data bit 4sample bit 4
5write data bit 5sample bit 5
6write data bit 6sample bit 6PWM output
7write data bit 7sample bit 7write strobe

Chip location

Controller Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux Mux tt_um_chip_rom (Chip ROM) tt_um_factory_test (TinyTapeout 05 Factory Test) tt_um_loopback (TinyTapeout 05 Loopback Test Module) tt_um_Leaky_Integrate_Fire_nfjesifb (Leaky Integrate and Fire Neuron Model) tt_um_topModuleKA (Time Multiplexed Neuron Ckt) tt_um_sap_1 (SAP-1 Computer) tt_um_lif (Leaky Integrate-and-Fire Neuron (Verilog Demo)) tt_um_jleugeri_ticktocktokens (TickTockTokens) tt_um_LSNN (Spiking LSTM Network) tt_um_if (Integrate-and-Fire Neuron. (Verilog Demo)) tt_um_mihailocode_neural_network (Neural network on chip) tt_um_hls_lfi (Simple Leaky Integrate and Fire (LIF) Neuron) tt_um_diadatp_spigot_e (e Spigot) tt_um_kskyou (Continued Fraction Calculator) tt_um_wokwi_380005495431181313 (Water Pump Controller) tt_um_EventFilter (Event Denoising Circuit) tt_um_wokwi_380416099936681985 (7 segment seconds (Verilog Demo)) tt_um_freq_hcohensa (Frequency Encoder/Decoder) tt_um_wokwi_380410498092232705 (UART Greeter with RNN Module) tt_um_wokwi_380120751165092865 (WS2812B LED strip driver) tt_um_wokwi_380408486941145089 (Tiny Tapeout 5 Workshop) tt_um_wokwi_380409169798008833 (Tiny Tapeout 1) tt_um_wokwi_380409488188706817 (Supercon Workshop) tt_um_matrix_multiplier (Matrix Multiplier) tt_um_wokwi_380408594272345089 (Clock Divider) tt_um_wokwi_380408784463076353 (Binary Counter) tt_um_wokwi_380408396356749313 (ring osc test) tt_um_7segx4_clock_abhishek_top (7 segment clock with 4 digits) tt_um_wokwi_380409481852161025 (test001) tt_um_hodgkin_huxley (Hodgkin-Huxley Chip Design) tt_um_wokwi_380408823952452609 (Character Selector) tt_um_wokwi_380409904919056385 (Intructouction to PRBS) tt_um_wokwi_380409081067502593 (tto5 Supercon Project) tt_um_jmadden173_delta_modulation (Delta Modulation Spike Encoding) tt_um_wokwi_380409086743445505 (GameOfLife) tt_um_reflex_game (Reflex Game) tt_um_wokwi_380409019830656001 (Logic Gates Tapeout) tt_um_Fiona_CMU (Stream Cipher w/ LSR) tt_um_wokwi_380409532780455937 (tt5modifyd) tt_um_alu_chip (ALU Chip) tt_um_wokwi_380408936929183745 (Tapeout Test) tt_um_rjmorgan11_calculator_chip (Calculator chip) tt_um_wokwi_380409369220404225 (Shifty Snakey) tt_um_synth_GyanepsaaS (Synth) tt_um_wokwi_380408774591779841 (Sawtooth Generator) tt_um_wokwi_380197591775930369 (Blinking A) tt_um_wokwi_380409393770716161 (Supercon 2023) tt_um_mvm (Sparsity Aware Matrix Vector Multiplication) tt_um_wokwi_380408455148316673 (Ring Oscillator and Clock Source Switch) tt_um_mv_mult_alrdelcr (Matrix Vector Multiplication (Verilog Demo)) tt_um_wokwi_380416361853146113 (IDK WHAT TO DO) tt_um_wokwi_379319062779062273 (7-segment display logic system ) tt_um_wokwi_380409568391147521 (Hardware Trojan Example) tt_um_wokwi_379824923824476161 (Analog Clock) tt_um_wokwi_380145600224164865 (7 segment display) tt_um_wokwi_379889284755158017 (W_Li_10/28) tt_um_wokwi_380408409844584449 (Supecon Gate Play) tt_um_manjushettar (ECE 183 - Integrate and Fire Network Chip Design) tt_um_wokwi_380409236812508161 (tto5) tt_um_rebel2_balanced_ternary_ALU (REBEL-2 Balanced Ternary ALU) tt_um_wokwi_380229599886002177 (Stochastic Multiplier) tt_um_jeffdi_seven_segment_seconds (7 segment seconds - count down) tt_um_wokwi_380416616536542209 (TT05 Submission) tt_um_lif_n (Leaky Integrate-and-Fire Neuron) tt_um_wokwi_379764885531576321 (Count via LFSR) tt_um_dlmiles_tt05_i2c_bert (I2C BERT) tt_um_loopback_ericsmi (tt05-loopback tile with input skew measurement) tt_um_flappy_vga_cutout1 (Flappy VGA) tt_um_async_proc_paulschulz (Asynchronous Parallel Processor Demonstrator) tt_um_wokwi_380055891603379201 (Hex Countdown) tt_um_nickjhay_processor (Matrix multiply coprocessor (8x8 1bit)) tt_um_htfab_cell_tester (Standard cell generator and tester) tt_um_wta (Winner-Take-All Network (Verilog Demo)) tt_um_muncherkin_lioncage (Lion cage) tt_um_seven_segment_seconds_ksandov4 (Brain Inspired Random Dropout Circuit) tt_um_seanvenadas (Event-Based Denoising Circuit) tt_um_wokwi_378231665807713281 (RAM cell test) tt_um_rejunity_ay8913 (Classic 8-bit era Programmable Sound Generator AY-3-8913) tt_um_rnn (RNN (Demo)) tt_um_gharenthi_top (STDP Neuron) tt_um_SNN (Basic Spiking Neural Network) tt_um_btflv_8bit_fp_adder (8 bit floating point adder) tt_um_perceptron (Perceptron Hardcoded) tt_um_jkprz (Cheap and quick STDP) tt_um_topLevel_derekabarca (Brain-Inspired Oscillatory Network) tt_um_uwuifier (UART uwuifier) tt_um_perceptron_connorguzi (Perceptron and basic binary neural network) tt_um_hadirkhan10_lif_neuron (Leaky Integrate-and-Fire Neuron) tt_um_wokwi_380119282165535745 (7 segment seconds) tt_um_uabc_electronica_2023 (UABC-ELECTRONICA) tt_um_proppy_bytebeat (bytebeat) tt_um_meriac_play_tune (Super Mario Tune on A Piezo Speaker) tt_um_wrapper_inputblackboxoutput (Byte Computer) tt_um_vhdl_seven_segment_seconds (7 segment seconds (VHDL Demo)) tt_um_cejmu (4-Bit ALU) tt_um_rejunity_sn76489 (Classic 8-bit era Programmable Sound Generator SN76489) tt_um_minipit_stevej (Miniature Programmable Interrupt Timer) tt_um_gchenfc_seven_segment_gerry (7-segment Name Display) tt_um_supertails_tetris (Tetris) tt_um_mabhari_seven_segment_seconds (Simple_Timer-MBA) tt_um_njzhu_uart (UART Receiver) tt_um_wokwi_376553022662786049 (AGL CorticoNeuro-1) tt_um_adaptive_lif (Leaky-Integrated Fire Neuron) tt_um_myuart (MyUART) tt_um_wokwi_380438365946734593 (UART test) tt_um_tkmheart (Heart Rhythm Analyzer) tt_um_stdp (Spike-timing dependent plasticity (Verilog Demo)) tt_um_wokwi_380465686251921409 (Tiny Tapeout 5 TM project1) tt_um_thermocouple (Thermocouple-to-temperature converter (digital backend)) tt_um_wokwi_380412382001715201 (Naive 8-bit Binary Counter) tt_um_asinghani_tinyscanchain_tt05 (tinyscanchain Test Design) tt_um_carlosgs99_cro_udg (6 digit chronometer.) tt_um_suhrojo (Convolutional Network Circuit Chip Design) tt_um_mvm_ (Matrix Vector Multiplication Accelerator) tt_um_perceptron_neuromeme (Perceptron (Neuromeme)) tt_um_czlucius_alu (4 Bit ALU) tt_um_BNNNeuron (Binary Neural Network (Verilog Demo)) tt_um_urish_skullfet (SkullFET) tt_um_ja1tye_sound_generator (Wavetable Sound Generator) tt_um_jaylennee_wta_pwm (PWM signal generation with Winner-Take-All selection) tt_um_joerdsonsilva_top (Multimode Modem) tt_um_toivoh_synth (Analog emulation monosynth) tt_um_tiny_game_of_life (Tiny Game of Life) tt_um_mingkaic1_stack_machine (Stack Machine) tt_um_morningjava_top (ChipTune) tt_um_urish_silife (Game of Life 8x8 (siLife)) tt_um_tt05_analog_test (TT05 Analog Testmacro (Ringo, DAC)) tt_um_wokwi_380409528895479809 (RBUART) tt_um_mattngaw_fp8 (8-bit Floating-Point Adder) tt_um_chip_inventor_music__6_bit_count (6 bit Counter and Piano Music created by Chip Inventor) tt_um_4_bit_pipeline_multiplier (4 Bit Pipelined Multiplier) tt_um_wokwi_380477805171811329 (2-Bit ALU + Dice) tt_um_wokwi_380490286828784641 (TT02 Wokwi 7seg remake) tt_um_wokwi_380361576213660673 (ping pong asic) tt_um_prg (A Boolean function based pseudo random number generator (PRNG)) tt_um_digital_clock_sellicott (Digital Desk Clock) tt_um_haozhezhu_top (4-bit FIFO/LIFO) tt_um_top_mole99 (One Sprite Pony) tt_um_GrayCounter_ariz207 (4 bit Sync Gray Code Counter) tt_um_clkdiv (Clock and Random Number Gen) tt_um_matt_divider_test (TT05 Analog Test) tt_um_tomkeddie_a (VGA Experiments) tt_um_rejunity_rule110 (Rule110 cell automata) tt_um_no_time_for_squares_tommythorn (No Time for Squares) tt_um_urish_silife_max (Game of Life 8x32 (siLife)) tt_um_gfg_development_tros (TROS) tt_um_chatgpt_snn_mtomlin5 (ChatGPT designed Spiking Neural Network) tt_um_ks_pyamnihc (Karplus-Strong String Synthesis) tt_um_dinogame (VGA Dino Game) tt_um_himanshu5_prog_chipTop (Dual Compute Unit) tt_um_rtfb_collatz (Collatz conjecture brute-forcer) tt_um_retospect_neurochip (Field Programmable Neural Array) tt_um_urish_dffram (DFFRAM Example (128 bytes)) tt_um_rejunity_snn (Chonky SNN) tt_um_hh (Hodgkin-Huxley Neuron) tt_um_wokwi_377426511818305537 (PRBS Generator) tt_um_devinatkin_stopwatch (Stop Watch) tt_um_algofoogle_vga_spi_rom (vga_spi_rom) tt_um_blink (RO and counter) tt_um_ttl74hc595_v2 (8-Bit Shift Register with Output Latches 74HC595) tt_um_psychogenic_neptuneproportional (Neptune guitar tuner (proportional window, version b, debug output on bidir pins, larger set of frequencies)) tt_um_urish_simon (Simon Says game) tt_um_kianV_rv32ima_uLinux_SoC (KianV uLinux SoC) tt_um_urish_ringosc_cnt (Ring oscillator with counter) tt_um_sunaofurukawa_cpu_8bit (cpu_8bit) tt_um_vga_clock (VGA clock) tt_um_seven_segment_seconds (7 segment seconds (Verilog Demo)) tt_um_frequency_counter (Frequency counter) tt_um_rgb_mixer (RGB Mixer) tt_um_MichaelBell_spi_peri (SPI Peripheral) tt_um_multiplexed_clock (Multiplexed clock) tt_um_psychogenic_shaman (Shaman: SHA-256 hasher) tt_um_yubex_metastability_experiment (metastability experiment) Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available Available