FM synthesis on Pokitto?

Hi there!

Right now I am thinking about some sound related projects to start learning to program the Pokitto.
At least two ideas are floating in my mind:

  1. Sequencer for modular synthesizers
    Looking at the GPIO head, I am wondering if it would be possible to use the Pokitto as a sequencer for analog modular gear. Basically one would have to send control voltages for the pitch of an analog oscillator and send a gate pulse of variable length to trigger an envelope. 3.3 Volts should be enough for a pitch range of three octaves in the Volt per octave system. But I am not so sure if 3.3 V would be enough to trigger an envelope generator. However, most modular systems feature one or more voltage controlled amplifiers.
    Actually I am not sure if the Pokitto is capable of doing that, but it would be nice to learn a bit about the GPIO head. In addition, one would have to program a simple tracker interface for composing.

  2. FM synthesis
    As far as I see the Pokitto features a 3 channel software synth provided by jonne and some PWM related sound output, but how about trying to implement FM synthesis? In its simplest form it consists of two sine oscillators, one is called the carrier and the other is the modulator. The frequency of the carrier is modulated by the modulator and by this way, complex timbres can be created. Modulating the ampltudes of the oscillators with envelopes allows for creating interesting evolving spectra.

So far I am leaning towards option two as I am interested in real time audio synthesis, but unfortunately know very little about hardware programming.

For now I am imagining a simple program that plays a sinewave that is either read from a wavetable or generated in real time by the Pokitto. These data are send to the DAC (headphone output). The frequency of the sine wave should be controllable via the buttons.

Of course one would have to keep in mind that later one would have to compute the modulated carrier signal (with envelopes and all) which is send through the DAC. It would also be nice if several of those two operator voices could be used in parallel later, possibly controlled by a tracker interface.

Any ideas for starting points?

1 Like

Yes. We simply have to write you a so-called “audio interrupt” function, that is called f (Hz) times per second.

Making a way to override the default audio interrupt should be pretty easy.

After we do that, you can take existing examples of FM modulation source code from the web and rewrite just the output portion.

Alternatively you can write directly to the buffer and use audioBufferedIRQ to output (ask @spinal , he has done it for his movieplayer)

And even though FM synthesis is computationally far more demanding than substractive synthesis, if we get @FManga to take a look at optimization and use some well thought look-up tables (for phase modulation), I am pretty confident we can do it.

Yes, exactly, I also thought about reading about phase modulation as it should lower the computational burden. There are certainly some low level tricks to make it work.

I hope @NullMember comes back from hiatus soon. He is also into sound synthesis, and we are finally getting to a point where things are becoming interesting.

Excuse my ignorance (I’m not really a hardware person) but wouldn’t using an optocoupler solve that?

Hey Pharap,

I am not a hardware person also, but from what I heard, optocouplers are used to seperate two circuits from each other optically. For instance, hardware MIDI interfaces use optocouplers to avoid ground loops. There may be an audible difference with analog equipment if one uses a MIDI interface with optocoupler or a direct connection between your computer and the equipment via USB (hum).

Curiously, optocouplers are also used in some modular equipment, for example low pass gates. There they are called “vactrols”. It is basically just a combination of a light emitting diode and a photoresistor. The idea is -as far as I can get it- that you can transmit signals optically without the need of having a shared circuit which may be problematic in some audio applications. Some people seem to like the response characteristics of vactrols, sonically. I think the idea is that the vactrol responds a bit differently from a direct electrical connection, because the signal is transmitted from an LED to a photoresistor.

However, I think that an optocoupler could be a nice idea for a Pokitto head circuit to protect the device from a voltage overload when recieving high voltage signals from analog modular equipment, for instance for clocking purposes.

1 Like

FM synthesis is the process by which FM radios are made, right? :stuck_out_tongue:
I can help with optimization, but somebody’s going to have to make a reference implementation first. I have no idea what this stuff does. :laughing:

1 Like

I know nothing of the hardware involved,
but I think this .gif (shamelessly stolen from Wikipedia) explains the general idea:

AM = Amplitude Modulation
FM = Frequency Modulation

So the amplitude/frequency is used like a data encoding.

Yes, indeed, I think FM was first used in radio applications but later John Chowning discovered in the audio domain that it could be used to create complex timbres from simple sine oscillators. His original article is quite interesting.

I thought a bit about how to implement FM on the Pockitto, but first I have to get the compiler working. But here is the general idea:

1.) Create an array of e.g. size 512 that contains the data for a sine wave (a wavetable).
2.) Scan the wavetable with and index and send that information out to the DAC. Frequency could be changed by manipulating the rate by which the the index traverses the buffer. For instance skipping every second step when reading the table would produce the 2nd harmonics, that is, the wave would should one octave higher than the wave in the table. Probably one would need some linear interpolation here if one wants to change the pitch in musical intervals.
3.) An additional phase offset controlled by another function (e.g. sine or some piecewise linear function) could be added to create a modulated output wave. I think this would be phase modulation or phase distortion.

There are some more questions here, for instance:

  • Do I need a ring buffer for storing the wave data?
  • How should the data be shaped, values from 0 to 255?
  • How to efficiently do the linear interpolation, can the Pockitto handle floating point operations?
  • How to send the data from a buffer to the DAG? How is the sampling frequency handled? What is the size of the audio buffer?
  • Is user input to change parameters in real time possible?

Well, the first goal is to send the information in the wavetable out to the DAC. Already have created some data.

Your basic idea is exactly correct.

But I think one fundamental design choice is the size of the sine table. In order to be able to modulate the sine wave, we should indeed increment/decrement the phase step. But, if the sine table is very short, there will not be enough resolution to make the modulation effect smoothly.

If, on the other hand, we use interpolation of floating point values, then on a small device like this with no FPU, you’ll kill the performance.

I think we need your idea, but a much longer sine table.

Yes, I also thought that the interpolation could be a problem. The longer table on the other hand could take away to much memory? However, I think this is not a big problem one could check out tables of different lengths. They are not difficult to produce.

Ha, so far I came up with this:

#include "Pokitto.h"
#include "Wavetable.h" // uint16_t wave data

Pokitto::Core game;
Pokitto::Display disp;

uint16_t pos = 0;

int main ()
{
    disp.persistence = true;
    disp.color = 7;
    disp.bgcolor = 0;
    disp.clear();

	game.begin();

	while (game.isRunning())
	{
		if (game.update())
		{
			disp.clear();

			disp.println("Position:");
			disp.print((int) pos);
			disp.print("\n");
			disp.println("Value:");
			disp.print((int) wavetable[pos]);

			if(pos++ == 512){pos=0;}

		}
	}
    return 0;
}

The program simply scans the wavetable and prints the position and the found value to the screen. However, it is running much too slow, probably because it is in some kind of game loop.
The good thing is that there is so much code around here…have do dig a bit deeper, but a good start so far. It is nice to see this running on hardware.

1 Like

When you make a wavetable, remember sine is symmetric. With a bit of trickery it is possible to get away with just a table for 0…pi/2 radians. If memory becomes an issue that is.

EDIT: but naturally, the fastest way is to have just a full table to increment through

2 Likes

I don’t know the answer to that, but I do know that you could use fixed points instead if needs be.

1 Like

You’re talking about DDS (Direct Digital Synthesis). I’ve wrote DDS example for pokitto. Floating point is not necessary. Like @Pharap says, fixed point is more efficient than floating point operations. Here is the example code:
Fixed point routines:

typedef uint32_t fix_t; // we don't need negative values so format is UQ16.16

const fix_t fix_one = 0x00010000;   // fixed point one
const fix_t fix44100 = 0xAC440000;  // fixed point 44100

static inline fix_t float_to_fix(float a)	  {return fix_t(float(a * fix_one));}
static inline fix_t int_to_fix(int a)	      {return fix_t(a * fix_one);}
static inline int fix_to_int(fix_t a)		  {return int(a >> 16);}
static inline float fix_to_float(fix_t a)	  {return float(a / 65536.0);}
static inline fix_t fix_add(fix_t a, fix_t b) {return a + b;}
static inline fix_t fix_sub(fix_t a, fix_t b) {return a - b;}
static inline fix_t fix_mul(fix_t a, fix_t b) {return fix_t((uint64_t(a) * uint64_t(b)) >> 16);}

DDS Class:

class DDS{
	private:
		fix_t sampleRate;
		fix_t tableSize;
		fix_t tdivs;        // table size / sample rate
		fix_t phaseAcc;
		fix_t tuningWord;
		fix_t frequency;
		const uint8_t * lookUpTable;
	public:
		DDS(uint8_t _freq, const uint8_t * _LUT);
		void freq(uint16_t _freq);
		uint8_t update();
};

DDS Functions:

inline DDS::DDS(uint8_t _freq, const uint8_t * _LUT){
	sampleRate = int_to_fix(44100);
	tableSize = int_to_fix(256);
	tdivs = 0x17C;			            // precalculated 256 / 44100 in UQ16.16
	frequency = int_to_fix(_freq);
	lookUpTable = _LUT;
	tuningWord = fix_mul(frequency, tdivs);
	phaseAcc = 0;
}

inline void DDS::freq(uint16_t _freq){
	frequency = int_to_fix(_freq);
	tuningWord = fix_mul(frequency, tdivs);
	return;
}

inline uint8_t DDS::update(){
    phaseAcc = phaseAcc + tuningWord;
    uint8_t index = fix_to_int(phaseAcc) & 0xFF
    return *(lookUpTable + index);
}

Basically when you change frequency tuning word calculated using this equation:
tuningWord = frequency * (tableSize / sampleRate)

You don’t need to deal with fixed point math. @Pharap wrote fixed point math library and it’s really easy to use.
I’m not tried FM synthesis yet but it’s not so hard. Just we need a signed fixed point numbers.

Edit: @jonne i’m back :slight_smile:

4 Likes

Thanks so much! I will read up on the topics as time allows, I already found some discriptions that look similar to your implementation. So far I got EmBitz working and am feeling tempted to check out the basic API features more as I am a total beginner. One idea is to implement the DDS code and to draw the generated wave to the dispay to check visually what the code is doing.

1 Like

Hmm, after reading the C code snippet that I found that conceptually describes how to implement an oscillator that is capable of doing AM, FM, and PM and reading your code, I tried to translate the C code to C++ but there are some open points regarding the types, because of the lack of a floating point unit on the Pokitto. In the original, double is used throughout except for index, which is long.

However, here is my translation attempt, I have marked things that I am not so sure of how to do in fixed point math by [?]. I am also not sure if the phase variable would not overflow?

#define PI 3.14159265358979
#define RAD_TO_INDEX (512.0 / (2.0 * PI))
/* Length of wavetable array is 512 */

class FM_Osc{
    private:
        [?] freq; /* osc freq in radians/sample*/
        [?] phase; /* accumulated osc phase in radians*/
        const uint8_t wavetable[512]; /* wavetable */

        [?] fm; /* freq mod input in radians/sample */
        [?] pm; /* phase mod input in radians */
        [?] am; /* amp mod input in any unit (Envelopes?) */
        [?] index; /* index to wavetable [0-511]*/
        [?] inst_freq; /* osc freq + freq mod*/
        [?] inst_phase; /* osc phase + phase mod*/
        [?] output; /* osc output */

    public:
        update([?]fm, [?]pm, [?]am);
}

inline uint8_t FM_Osc::update([?]fm, [?]pm, [?]am)
{
    inst_freq = freq + fm; /* Frequency modulation */
    phase += inst_freq; /* accumulate phase */
    inst_phase = phase + pm; /* Phase modulation */

    /* convert to lookup table index */
    index = RAD_TO_INDEX * inst_phase;
    index &= 511; /* mod 512 */

    output = wavetable[index] * am; /* Amplitude modulation */

    return(output);
}

/* radians/sample= Osc_freq in Hz * ((2 * PI )/ Sample Rate in HZ) */

Try to write everything in floating point math then we will convert your code to fixed point together. I’m not tried @Pharap’s fixed point library yet but I looked to source and it’s like using floating point math. Because of this it’s really easy to convert your code.
Phase variable overflows but it’s not a problem if you use 2^x table size and unsigned variables. For example if your table size is 16, add 0b0011 (tuning word) to 0b1110 (old phase acc) result is 0b10001 (new phase acc). Result & 0xF = 0b0001. It’s what we want.
I need to sleep now :sleeping:

2 Likes

That was precisely the reason I wrote it.

At one point in time, the words “fixed point maths” were thrown around a lot on the Arduboy forums without anyone attempting to provide any references or explain how it works.
I looked at some existing libraries and most of the ones I found used clunky C-style interfaces.
So I decided that I would challenge myself to learn how they work and write a nice C++ style library using templates and operator overloading in an attempt to make them as accessible as possible.
It took me a long time to develop it (~3 months on and off) but the end result was worth it.

In some cases it’s actually possible to simply do a find and replace on float and exchange it with one of the fixed point types.
Often the code will compile with slight errorsresulting from precision differences, but those can usually be evened out or accounted for.


I need to get back to developing it some time.
I keep getting involved in new projects before I’ve done what I need to on old ones.

2 Likes

Writing fixed point library easier than using it (with library or not :sweat_smile:) most of time :smile:
@gho drawing output to screen is really good idea. Accessing low level audio routines are harder than it.

1 Like