# Guide to develop 4k Intros for MorphOS (Part 2)

Once upon a time, a 4k intro that didn't have music, it was a very sad intro, because it couldn't dance...

To solve this problem and bring some fun and dance to our intro, I made a small and simple 4k synth.

How it works?

All the song is defined on two arrays, sequences of notes, and patterns.

Patterns contains all patterns with notes to build the song, it's similar to patterns on nanoloop or protracker, numeric values are used as notes, start from 1 to 254, 255 means that the sound is stopped on this position note, and 0 means that last note remain played.

Patterns array for this piece of a familiar song to all C64 users, looks like this (SILENCE notes are not used):

First some defines need:

Code:

#define TONEFREQ     220.0f
#define TONEBASE     1.059463094359f

#define CHANNELS 2
#define PATTERNS 5
#define PATTERNLEN 16
#define SONGLEN  4

#define NOTELEN 2048
#define PATLEN  32768

#define SILENT     255

The notes for our song:

Code:

static char patterns[PATTERNS][PATTERNLEN]=
{
{13, 18, 20, 18, 25, 18,20,18,13, 18, 20, 18, 25, 18,20,18,},
{22, 0, 0, 0, 0, 0,  20,0, 23,22,0, 20, 0,0,22,18},
{0,0, 0,0,0,0, 18, 18, 18,17,0,15, 0, 0, 17,  18,},
{0, 0, 0,0, 0, 0, 0, 0, 0,13,15,13,20,18,13,20},
{18, 0, 0,0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0},
};

And this is the pattern song, very simple:

Code:

static char seq[CHANNELS][SONGLEN] =
{
{0, 0, 0, 0},
{1, 2, 3, 4}
};

The song uses two channels with four patterns. The first channel repeats pattern 0 continously (first row on patterns) and the second channel plays patterns 1, 2, 3 and 4 (the rest of patterns defined on the array)

The synth apply an envelope filter with 4 phases, attack, decay, sustain and release, this filter is defined for every channel here:

Code:

static float env_chan[CHANNELS] =
{
{
0, 0, // a
256, 1, // d
2048, 0.2, // s
3072, 0.6, // r
},
{
0, 0, // a
500, 1, // d
7000, 0.8, // s
8000, 0.7 // r
}
};

Every phase have two values, the first value, is the pointer of the current note when this phase starts, and second is the volume applied to this phase, from 0 to 1.

And here is where magic happens, the code are commented, so if you have any question, go ahead!

Code:

static short pos;
static float freq;
static char note;
static short note_pos;

static byte ch;
static float env,hz;
static float *env_val;
static char pat_ptr;

// Two oscillators are availabe, sin and saw
// first parameter are hertzs and second parameter is time

static float osc_sin(float f, int tm)
{
int d = tm*720*f/8000;
return (sinus[(d%720)]+1.0)/2.0;
}

static float osc_saw(float f, int tm)
{
int r=(2*FREQUENCY / f);

return (tm % r) * (1.0/r);
}

static void synth4k(void)
{
// Fill the audio buffer
for (n = 0; n < BUFFERSIZE; n++)
{
// Set a pointer to the current buffer
// and set the current buffer to zero
byte *pb = &p1[n];
*pb = 0;

// Process our song to generate a wave for every channel
for (ch = 0; ch < CHANNELS; ch++)
{
// Set some pointers to save some bytes,
// note contains the current note played on this channel
// po is the current position of the wave note for filling our buffer
// pat_ptr contains the current pattern note to be played on this channel
char *not = &note[ch];
short *po = &pos[ch];
note_pos = (t>>11)%PATTERNLEN;
pat_ptr = patterns[seq[ch][(t>>15)%SONGLEN]][note_pos];

// If note is silence, don't process the song
if (pat_ptr < 255)
{
// Change the current note to be played,
// if it's zero the last note is preserved
if (pat_ptr > 0 && t%NOTELEN == 0)
{
*not = ((pat_ptr&63)-1);
*po = 0;
}

// Apply the envelope filter
env_val = env_chan[ch];
env = env_val;
for (i=0; i<8; i+=2)
{
if (env_val[i] > *po)
{
float pr = ((*po-env_val[i-2])/(env_val[i] - env_val[i-2]));
env = pr * (env_val[i+1] - env_val[i-1]) + env_val[i-1];
}
}
// Set base frequency
freq = TONEBASE;
// Get the frequency for the note to be played,
// it is a simple power function, but it is not available.
for (j = 1; j < *not; j ++)
freq *= TONEBASE;
// Get the herzs need to play the specific note
hz = TONEFREQ*freq;

// Here we define the instruments for the channels,
// Play with oscillators and apply more to the time,
// or hz parameters, add different oscillators, use
// your imagination to obtain different sounds!
if (ch == 0)
*pb+=env*(osc_saw(hz,t))*48;
else
*pb+=env*(osc_sin(hz,t+osc_sin(5,t)*10))*24;
}
// Increment the buffer position
*po+=1;
}
// Increment the time position
t++;
}
}

The instruments are defined on synth4k, this is for channel 0:

*pb+=env*(osc_saw(hz,t))*48;

and for channel 1:

*pb+=env*(osc_sin(hz,t+osc_sin(5,t)*10))*24;

As you can see, instruments are composed of 3 parts, env*osc*volume, osc is the part where you can use your imagination to build your instruments, applying some oscillators on time, hertzs, adding different oscillators, ...

To modify the octave, you should change TONEFREQ, it have an initial value of 220, 110 is an octave down, and 440 is an octave up.

And here are all files available to build the startup code and synthetizer.

4k intro tutorial 2

The story continues in part 3...