** WELCOME TO GRADRIGO **
-------------------------
 
Depending on how you use it, Gradrigo can function as a synthesizer (VST3), sequencer, audio
middleware, and a live coding platform. Gradrigo is an audio synthesis engine for interactive
chiptunes and chiptune sound effects. Gradrigo feels a bit like writing music in Paul Slocum's
Atari 2600 Sequencer Kit but it's platform-agnostic. A bit like Viznut's ByteBeat but more
intuitive. A bit like SuperCollider, but waaay simpler and chiptune-oriented.
 
You define sound by writing piece of code in Gradrigo Programming Language. It's a very simple
language. Think of it as something slightly higher than assembler.
 
|| Parts of this text marked like this "// ||" are only relevant to the Gradrigo VST or
|| Gradrigo Standalone App.
 
If you're reading this text as a page on the web, please consider downloading the Gradrigo
Standalone App and continue there. You'll be able to test the examples directly in it.
 
++----------------------------------------------------------------------------------------++
** / \ SAFETY WARNING: As with anything that synthesizes audio, please make sure to **
** / | \ LISTEN TO THE OUTPUT AT A LOW VOLUME. Very loud noises can be generated using **
** / . \ Gradrigo, especially when debugging your sounds. **
++----------------------------------------------------------------------------------------++
 
To use the Gradrigo VST or Gradrigo Standalone App:
---------------------------------------------------
 
0. GET IT HERE:
https://adam.sporka.eu/gradrigo.html
(Adam Sporka's home page)
1. WRITE CODE
You can use this editor
2. COMPILE
Menu: Engine > Recompile (Ctrl+R)
3. HEAR SOUNDS
By pressing one of the buttons that appear on the right
(or by pressing MIDI keys or routing MIDI events from your DAW, about that later)
 
To use Gradrigo for Unity:
--------------------------
 
0. GET IT HERE:
https://u3d.as/2E6f
(Unity Asset Store)
1. DESIGN YOUR SOUNDS
Use Gradrigo Standalone App or Gradrigo VST. While this is not necessary for using
Gradrigo in Unity, it's definitely much easier to design your sounds outside Unity first.
2. INSTANTIATE GRADRIGO IN YOUR GAME
Either download a .zip file from my website or get it from the Asset Store
3. TRIGGER SOUNDS USING API
 
A tutorial is here:
https://www.youtube.com/watch?v=xfXn7zsSVXo
 
Here is a very simple 8-bit kaboom sound:
 
kaboom = dur(~1) {
@loop
noise5(%a)
%a = %a + 0.01
}
 
And here is a bouncy sound with random pitch:
 
random = {
%d = ~0.1 + ? * ~0.1
%n = C4 + ? * 24 dur(%d) { vol(1.0) fadeout(%d) @loop sqr(#%n) }
%n = %n + 1 dur(%d) { vol(0.5) fadeout(%d) @loop sqr(#%n) }
%n = %n + 1 dur(%d) { vol(0.2) fadeout(%d) @loop sqr(#%n) }
}
 
|| By the right edge of this window you should see two buttons, "kaboom" and "blip".
|| Those are the compiled sounds, ready to be played back. (Please note that the boxes
|| on the right are always sorted alphabetically, regardless of their position
|| in the script.)
 
|| When you change the code in this editor, just press Ctrl+R to have it recompiled.
|| Immediately below this editor you will see if your code has any syntax errors.
 
Gradrigo is a parallel grammar-driven hierarchically concatenative synthesizer.
-- Grammar-driven, because it interprets pieces of code as audio output
-- Hierarchically concatenative, because it produces the complex audio output as concatenation
of shorter pieces, those are produced by even shorter pieces, etc., and somewhere deep down
there are actual waveforms.
-- Parallel, because multiple pieces of code can run simultaneously (and also interact between
each other.)
 
|| Intrigued? Great! Choose "Next Lesson" in the Tutorial menu
 
** BOXES AND SEQUENCES **
-------------------------
 
The boxes are the units of code that produce sound.
 
A box is identified by its name and contains a sequence (or more sequences) of output samples,
commands, other statements. A sequence is delimited by curly brackets {}.
 
Let's start by "drawing" the waveform to be played back, sample-by-sample. `_` and `-` are
the minimum and maximum amplitudes. The following will produce a short blip of square wave.
The box is called "short". Please note that those are `backquotes`, not 'apostrophes':
 
short = {
`________--------________--------________--------________--------________--------________--------`
`________--------________--------________--------________--------________--------________--------`
`________--------________--------________--------________--------________--------________--------`
`________--------________--------________--------________--------________--------________--------`
}
 
Writing your square wave this way brings you really, really close to the audio signal.
It's like pixel art of audio. It shows how much data is needed to describe even the shortest
of the samples.
 
The "short" box will produce a tiny blip of sound because we specified only 384 samples.
At 48000 Hz it would last only 0.008 seconds.
 
We can chain the instances of "short" to produce something "longer". The boxes are concatenated
back-to-back.
 
longer = {
short short short short short short short short short short short short
short short short short short short short short short short short short
}
 
To make things really long, it is convenient to use the @loop label.
Note the "dur(24000)" which will limit the total number of samples produced by this box to
24,000 (0.5 seconds at 48,000 Hz):
 
looped = dur(24000) {
@loop // <--- Gradrigo will repeat whatever follows the label @loop indefinitely
`________--------` // <--- 16 samples of the square waveform
}
 
Note: When parameter of dur() is bigger than the number of samples that the sequence would
produce, the remaining duration is padded with zero samples. The following box will be 24000
samples long eventhough only the first 64 are specified and will be non-zero. This is useful
when aligning the blips like this in longer sequences of boxes.
 
padded = dur(24000) {
`________--------________--------________--------________--------`
}
 
Let's "draw" a different shape of the signal. Digits `1` through `9` correspond to different
sample values. `1` is the minimum and `9` is the maximum. The `_` and `-` are synonyms of `1`
and `9` respectively. The following produces a sawtooth-ish sound: 8 samples of ramp up
followed by 8 samples of the minimum. Compare the sound of "longer" and "longer_odd".
 
odd = {
`12345678________12345678________12345678________12345678________12345678________12345678________`
`12345678________12345678________12345678________12345678________12345678________12345678________`
`12345678________12345678________12345678________12345678________12345678________12345678________`
`12345678________12345678________12345678________12345678________12345678________12345678________`
}
 
longer_odd = {
odd odd odd odd odd odd odd odd odd odd odd odd
odd odd odd odd odd odd odd odd odd odd odd odd
}
 
Let's try some more fun stuff with boxes in the next lesson.
 
** BOXES, MORE EXAMPLES **
--------------------------
 
(In the previous lessons I indented the code of the boxes by three spaces from the left to make
things more legible. Gradrigo ignores the amount of non-printable characters between statements.
From now on, the longer examples won't be indented anymore.)
 
And you've probably already guessed that the double dashes are line comments.
 
Anyway, here I define 8 different wave form periods of the identical length. "a" through "g"
would be tones, "z" is a silence. (Remember the previous chapter, where it was defined that
`5` is in the middle between `1` and `9`, where `1` was the minimum, `9` was the maximum, and
that `1` == `_` and `9` == `-`.)
 
a = { `________________________________________________------------------------------------------------` }
b = { `________________________------------------------________________________------------------------` }
c = { `_______________-----------------________________----------------________________----------------` }
d = { `____________------------____________------------____________------------____________------------` }
e = { `________--------________--------________--------________--------________--------________--------` }
f = { `______------______------______------______------______------______------______------______------` }
g = { `____----____----____----____----____----____----____----____----____----____----____----____----` }
z = { `555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555` }
 
Let's chain them up:
 
A = { `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa` }
B = { `bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb` }
C = { `cccccccccccccccccccccccccccccccc` }
D = { `dddddddddddddddddddddddddddddddd` }
E = { `eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee` }
F = { `ffffffffffffffffffffffffffffffff` }
G = { `gggggggggggggggggggggggggggggggg` }
Z = { `zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz` }
 
And now, let's make some patterns:
 
X1 = { `AAAABBBBAAAABBBBAAAABBBBAAAABBBB` }
X2 = { `AAAAZZZZAAAAZZZZAAAAZZZZAAAAZZZZ` }
X3 = { `ABCDEFGZABCDEFGZABCDEFGZABCDEFGZ` }
X4 = { `AEBDACDEFGCABGDFCBAGFEFCDABGEFDC` }
X5 = { `AABBCCDDAABBCCDDCCDDEEFFCCDDEEFF` }
 
BTW, the `-notation is a shorthand for chaining the patterns with single-letter names. The box
"X6" is identical to "X5":
 
X6 = { A A B B C C D D A A B B C C D D C C D D E E F F C C D D E E F F }
 
When naming boxes, please be aware that Grarigo reserves the uppercase note names to denote
actual MIDI values associated with them. C4 = 60, C#4 = 61, Eb4 = 63, etc. We will see how
to use those soon.
 
NOISE
 
When `?` produces a random sample value between -1 and 1. Writing those one after another
produces white noise.
 
r = { `????????????????????????????????????????????????????????????????????????????????????????????????` }
R = { `rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr` }
 
Let's make a veeery rudimentary chiptune which will sound like something straight out
of the ATARI 2600:
 
chiptune = { `BBBB RZAA BBBB RZZZ BBBB RZAA BBBB RZZZ
CCBB RAZZ CCBB RAZZ BBZZ RBZZ BBZZ RZZZ`}
 
** SQUARE WAVE OSCILLATOR **
----------------------------
 
Producing sound effects as shown in the previous lessons is fun but not very practical.
Gradrigo has a few built-in "oscillators".
 
Writing the name of the oscillator (sqr in the following example) makes the box produce
exactly one sample of that waveform. The number in () parentheses is the length of the period
in samples. Writing this:
sqr(6) sqr(6) sqr(6) sqr(6) sqr(6) sqr(6) sqr(6) sqr(6) sqr(6) sqr(6) sqr(6) sqr(6)
is the same as writing this:
`___---___---`
 
Typing this would make 128 samples of square wave with the period of 8 samples:
 
bad = {
sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8)
sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8)
sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8)
sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8)
sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8)
sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8)
sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8)
sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8) sqr(8)
}
 
Very awkward indeed. Let's use the combination of dur() and @loop to save us some typing:
 
better = dur(4800) {
@loop
sqr(40)
}
 
** MIDI NOTE NUMBERS AND NAMES **
---------------------------------
 
To make a note of a given pitch we need to know how many samples the waveform should last.
To make it easier, Gradrigo has a built-in operator # which converts the MIDI note to the
coresponding note of samples of the period closest to that note. The following will play note
C4 which is MIDI=60 (according to Roland, not YAMAHA).
 
cee_four_1 = dur(4800) {
@loop
sqr(#60)
}
 
So that we don't need to have a MIDI table handy, we can type note names directly instead
of their MIDI numbers:
 
cee_four_2 = dur(4800) {
@loop
sqr(#C4)
}
 
Let's finish with some longer sequence:
 
melody = {
dur(10000) { @loop sqr(#D4) }
dur(10000) { @loop sqr(#C4) }
dur(10000) { @loop sqr(#B3) }
dur(10000) { @loop sqr(#C4) }
dur(40000) { @loop sqr(#Eb4) }
dur(10000) { @loop sqr(#F4) }
dur(10000) { @loop sqr(#Eb4) }
dur(10000) { @loop sqr(#D4) }
dur(10000) { @loop sqr(#Eb4) }
dur(40000) { @loop sqr(#G4) }
}
 
Note that dur() can be inside a box multiple times, followed by
an unnamed sequence { } with its own loop of things happening inside. dur() makes sure
that the duration won't be exceeded.
 
** OSCILLATORS sqr, saw, noise5, and noise17e **
------------------------------------------------
 
In addition to sqr() oscillator, there are three more. Here's the complete list:
oscillator behavior
---------- --------
sqr Square wave generator = -1 for 1/2 of the period, 1 for the other 1/2
saw Upward sawtooth generator = Slope from -1 to 1 with the length of the period
noise5 5-bit noise generator from ATARI 800XL
noise17e Simulation of 17-bit noise generator from ATARI 800XL
 
square_wave = dur(48000) {
@loop
sqr(#C5)
}
sawtooth_wave = dur(48000) {
@loop
saw(#C5)
}
noise5_wave = dur(48000) {
@loop
noise5(#C5)
}
noise17e_wave = dur(48000) {
@loop
noise17e(#C5)
}
 
** TIMING IN GRADRIGO **
------------------------
 
Gradrigo always internally works with the number of samples. But this is awkward for specifying
pitches as well as for specifying durations.
 
For this, the following operators have been defined:
operator meaning example depends on
-------- ------- ------- ----------
~ number of seconds ~2 sample rate
! number of 1/4 notes !2 sample rate, BPM
@ 1 period of frequency @440 sample rate
# 1 period of MIDI note number #Eb4 sample rate
 
The sample rate is set automatically based on your DAW or your Audio Settings. However, please
note that Gradrigo at 44,100 Hz sounds slightly different than Gradrigo at 48,000 Hz because:
>>> THE TIMING OF EVERYTHING WORKS WITH INTEGER NUMBER OF SAMPLES <<<
To produce 1 second at 48,000 Hz you need to produce 48,000 samples. To produce 1 second
at 44,100 Hz you need to produce 44,100 Hz. Easy enough.
But producing the period corresponding to 440 Hz means to produce 0.002272727 sec. Gradrigo
only works which integers. And so, at 48,000 Hz the period would be 109 samples while
at 44,100 Hz the period will be 100 samples. However, none of those durations would corespond
to 440 Hz exactly. This is one of the key defining characteristics of Gradrigo, to which
the synthesizer owes for its late 1970s acoustic aesthetic.
 
one_second = dur(~1) {
@loop
sqr(#C4)
}
 
Gradrigo currently doesn't receive the BPM from DAW. By default it operats at 120 BPM.
To set something else you can use global command "bpm". Let's work with 115.5 BPM for now:
 
bpm(115.5)
 
one_eighth_note = dur(!0.5) { // !1 means 1/4 ==> !0.5 means 1/8 ==> !0.25 means 1/16
@loop
sqr(#C4)
}
 
** VOLUME LEVEL **
------------------
 
Internally, Gradrigo represents the output as 32-bit floats, -1 .. 1 being the default
amplitude range of the output from each box. In order to decrease (or increase) this range,
use the vol() command.
 
full = dur(~1) {
@loop
sqr(#C4)
}
half = dur(~1) {
vol(0.5)
@loop
sqr(#C4)
}
silent = dur(~1) {
vol(0.0)
@loop
sqr(#C4)
}
 
The vol() command affects everything in the box _after_ it:
 
steps = {
vol(0.25)
dur(~0.25) { @loop sqr(#C4) }
vol(0.5)
dur(~0.25) { @loop sqr(#C4) }
vol(1.0)
dur(~0.25) { @loop sqr(#C4) }
}
 
** FADE-OUT AND FADE-IN **
--------------------------
 
Gradrigo does not have envelope generators in the classical sense. It does have volume faders.
Fade-out decreases smoothly over time with each sample produced by that box and stops when
the volume reaches zero. The parameter of fadeout() is the number of samples it takes to go
from 1.0 all the way to 0.0. Of course, you can use ~ ! @ # operators when specifying this.
 
faded_out = dur(~1) { // One second
fadeout(~1)
@loop
sqr(#C4)
}
 
Fade-in does the opposite.
 
faded_in = dur(~1) {
vol(0.0) // In order to hear the fade-in, the volume must be less than 1.0
fadein(~1)
@loop
sqr(#C4)
}
 
vol(), fadein() and fadeout() operate in the context of the sequence they were mentioned.
 
faded_in_out = {
faded_in
full
faded_out
}
 
** POLYPHONY **
---------------
 
In one of the first chapters it was mentioned that the box can contain one or more sequences.
 
A sequence inside another sequence has been fairly common in the examples so far. The
following example first produces half a second of C4 and then another half a second of E4.
(Please note that the dur() statement is a condition of the sequence it precedes.)
 
outer_and_inner = {
dur(~0.5) { @loop sqr(#C4) }
dur(~0.5) { @loop sqr(#E4) }
}
 
Two "outer" sequences written one after another will play simultaneously as a sample-wise
sum of the outputs of the two sequences. The following produces half a second of concord C-E:
 
two_outer = dur(~0.5)
{ @loop sqr(#C4) }
{ @loop sqr(#E4) }
Technically, the dur(~0.5) belongs to the first sequence, however the first sequence of the
box determines the duration of the entire box. Should the other sequence(s) be longer than
the first one (i.e. should they produce more samples than the first one), their output is
cropped simultaneously with the end of the first sequence.
 
** VARIABLES **
---------------
 
There are two types of variables, global and voice-based. For now, variables may be only
called by single letters. I will change this in the future, so that they're less awkward
to use. The names are case-sensitive: %A, %B, %C are different variables than %a, %b, %c.
 
** GLOBAL VARIABLES **
----------------------
 
The global variables are declared using command global(). Once declared, they're visible
and shared by all the boxes:
 
global(%N, 60) // Declare variable and set it to a value
 
** EXPRESSIONS **
-----------------
 
First, let's make a box that plays a note whose pitch will be controlled
by this global variable:
 
play_note = dur(~0.1) {
@loop
fadeout(~0.1)
sqr(#%N)
}
 
Inside a box you can set the value of the global variables using mathematical expressions.
Each box then triggers the "play_note" box with an updated value of %N:
 
note_C5 = {
%N = C5
play_note
}
note_up = {
%N = %N + 1
play_note
}
note_down = {
%N = %N - 1
play_note
}
note_random = {
%N = C4 + ? * 12 // ? represents a random number between 0 and 1.
play_note
}
 
** SEQUENCE-BASED VARIABLES **
------------------------------
 
A sequence-based variable is any variable that is not global and is first mentioned in
a sequence. It is then visible inside that sequence and any of its subsequences. In the
following example, %f is visible throughout the box, whereas %g is visible only inside
the inner sequence. The first time it's used outside that inner sequence, it's value
will be 0 (default initial value).
 
There are two reserved variables used for MIDI communication. It is advisable not to use
them for anything else.
-- %n ... MIDI note number
-- %v ... velocity of the note
 
example = {
%f = C4
{
%g = E4
}
dur(4800) { @loop sqr(#%f) }
dur(4800) { @loop sqr(#%g) }
}
 
** MORE EXAMPLES **
-------------------
 
Let's get back to the example from the opening lesson: What happens here is the following:
-- The entire box is 1 second long
-- There's a loop inside in which:
* A noise5 oscillator generates an output sample with parameter %a
* The value of that parameter is increased by 0.01
 
kaboom = dur(~1) {
@loop
noise5(%a)
%a = %a + 0.01
}
 
Here's another somewhat more complex example:
 
pew5times = dur(~1) {
%a = 80
@loop
dur(~0.2) {
fadeout(~0.2)
%b = #%a
@loop
sqr(%b)
%b = %b + 0.01
}
%a = %a + 5
}
 
** LIST OF OPERATIONS **
------------------------
 
Unary operations
-X ... negative
~X ... seconds to samples (mainly for timing)
!X ... beats to samples (mainly for timing)
@X ... period @ freq. to samples (mainly for synthesis)
#X ... MIDI note numbers to samples (mainly for synthesis)
 
Binary operations
X + Y
X - Y
X * Y
X / Y
X \ Y ... X modulo Y
 
Function
sin2pi(X) ... returns sin(2*PI*X). (Implemented as a lookup table.)
 
** LIMITATIONS **
-----------------
 
* Currently, only float numbers can be used as values of the variables. I might add more
types in the future.
* There are not proper cycles in Gradrigo just yet. I think that at some point not too
far from now I'll add proper while() and for() statements.
* Also, I'll add if/else conditions.
 
** PASSING PARAMETERS TO BOXES **
---------------------------------
 
Boxes can receive parameters that could affect how the sound effects play. There are two
boxes in the following example. "parametrized" receives two values, %a and %n, and the
box "used_this_way" calls "parametrized" three times, with different combinations of
parameters.
 
parametrized:%a:%n = dur(~1) {
%a = 1 / %a
@loop
dur(~%a) {
fadeout(~%a)
@loop
sqr(#%n)
}
%n = %n + 1
}
used_this_way = {
parametrized:2:C3
parametrized:4:C4
parametrized:8:C5
}
 
>> Tip: When you compile this script, not only you'll see the list of the boxes in the
>> right panel but each box will have its parameters exposed as edit boxes. You can enter
>> those and then hit the play button to test various combinations of values with your
>> box.
 
Typically, this can be used for playback of melodies, as in the following example.
The box "note" plays a single note where the first parameter determines its duration
in the count of 1/16 notes and the second parameter is the MIDI note number of that note.
 
note:%d:%w = {
%d = !(%d * .25)
dur(%d) {
fadeout(%d)
@loop sqr(#%w)
}
}
 
In order to save lots of typing, Gradrigo utilizes the following shorthand:
A number (or the note name which is just a synonym of a numeric value) which is
written immediately after the last parameter of a box is interpreted as the wish of
the programmer to repeat the entire box with the same parameters as before, except
for the last one:
 
ode_to_joy = {
note: 2: E4 E4 F4 G4 G4 F4 E4 D4 C4 C4 D4 E4 E4 D4 D4
}
And in the following example, the note is re-triggered for different durations
of the notes too.
 
ode_to_joy_2 = {
note: 2: E4 E4 F4 G4 G4 F4 E4 D4 C4 C4 D4 E4 3: E4 1: D4 4: D4
}
It might be more legible to write this in multiple lines of code. The following
is the Gradrigo Jingle:
 
gr_jingle = {
note:
6: A5
6: D5
2: 0
1: A5 C6
2: B5 A5 G5
4: D5
}
 
You can interleave the boxes with additional commands and/or statements, such as
the volume information:
 
gr_jingle_dyn = {
note:
6: A5
vol(.75)
6: D5
2: 0
1: A5 C6
vol(.9)
2: B5 A5 G5
vol(.7)
4: D5
}
 
** MIDI INPUT AND USE IN A DAW **
---------------------------------
 
If you're using this as a standalone Windows desktop application, you might want to plug in
your MIDI keyboard now. If you're using this as a VST3 plugin in your DAW, make sure to enable
routing of the MIDI events to this plugin.
 
>> Tip for standalone app users: Go to menu "Options", choose "Audio and MIDI Settings ..."
>> and then enable your MIDI input. If you don't see your device listed, try restarting this
>> app.
 
Gradrigo has a straightforward way of connecting MIDI to boxes. The following code will turn
Gradrigo into a polyphonic synthesizer where half of the keyboard plays a square and the other
plays sawtooth.
 
square = {
vol(%v)
@loop
sqr(#%n)
}
sawtooth = {
vol(%v)
@loop
saw(#%n)
}
 
midikey(0, 59, square)
midikey(60, 127, sawtooth)
 
Note how neither "square" or "sawtooth" box have any dur() statement in it. They could have
but they don't have to. An instance of the box starts when a key is pressed and is stopped
when the key is released. What happens inside is that the box simply stops iterating. The
moment the key is released, the sequence runs its course and stops.
 
** @release LABEL **
--------------------
 
If you wish to add a bit of sound after the key is released, you might need to use the label
@release. While the key is held, the sequence iterates over the loop that starts at the @loop
label and stops before the @release label. Once the key is released, the execution continues
with what follows the @release label, in this case a short fadeout.
 
The following example is a square wave synth with 0.1s attack and 0.1s release:
 
attack_release = {
dur(~0.1) {
vol(0.0)
@loop
fadein(~0.1)
sqr(#%n)
}
@loop
sqr(#%n)
@release dur(~0.1) {
fadeout(~0.1)
@loop
sqr(#%n)
}
}
 
** LIMITING NUMBER OF VOICES **
-------------------------------
 
As the scripts get more complicated, playing too many voices at the same time, Gradrigo
may quickly run into performance issues. "maxinst" box condition will ensure that for
the given box, only the given number instances or less may play at the same time.
Voices that get over this limit will be released. (See also the DX7-esque example patch.)
 
voice_limited maxinst(4) = {
vol(%v)
@loop
sqr(#%n)
}
 
Uncomment the following line and recompile the example to hear it:
midikey(0, 127, attack_release)
 
** LIVE CODING **
-----------------
 
Gradrigo can recompile the script (Ctrl+R) while the music is playing without causing
to glitch. The idea is to run an endless loop and while this loop is running, you'd change
the contents of the boxes the loop and its boxes are using. The loop can be a multi-voice
box and then you can simply comment out the voices you don't wish to play.
 
|| Tip: Use "Options > Enter Note Namse using MIDI" function to have your MIDI keyboard enter
|| the names of the notes you press. It's way faster than typing the note names on the PC
|| keyboard. A single space character is entered after each note.
 
|| And now, run "music" box and then read the comments in this script to see which part
|| does what.
 
bpm(110) // Not too slow, not too fast
global(%T) // This global variable will be used to specify the transposition offset
// Percussion sounds
// Their boxes have single-letter names so that it's easier to type legible rhythmic
// patterns later on
 
// Bass drum
b = dur(!.25) {
own
%a = 260
fadeout(!.2)
@loop
sqr(%a)
%a = %a + 0.1
}
// Snare drum
s = dur(!.25) {
own
%a = 25
fadeout(!.25)
@loop
noise5(%a) `?`
}
// Hihat
h = dur(!.25) {
fadeout(!.0625)
@loop
`?`
}
// Open hihat
o = dur(!.25) {
vol(0.5)
fadeout(!.5)
@loop
`?`
}
// Silence (Using . will make it easy to read the drum patter later on)
. = dur(!.25) {
@loop `5`
}
// Transposed note (used for tranposed accompaniment)
// %d = duration 1 = whole note, 2 = half note, 4 = quarter note, 8 = 1/8th etc.
// %x = MIDI number
n:%d:%x = {
own
%d = !(4/%d)
%x = #(%x + %T)
dur(%d) {
@loop
fadeout(!0.25)
sqr(%x)
}
}
// Non-transposed note (used for the main melody)
m:%d:%x = {
own
%d = !(4/%d)
%x = #%x
dur(%d) {
@loop
fadeout(!0.25)
sqr(%x)
}
}
// Drum patterns
bb = { `b... b... b... b...` }
hh = { `.hhh .hhh .hhh .hhh` }
oh = { `.hoh .hoh .hoh .hoh` }
sd = { `.... s... .... s..s` }
// Bass + accompaniment
bass = { n:8: D2 D2 D2 D3 D2 D2 D3 D2 }
chr1 = { vol(.25) n:16: D3 D3 D3 D3 D3 D3 D4 D4 D3 D3 D3 D3 D3 D3 D4 D4 }
chr2 = { vol(.25) n:16: A3 A3 A3 A3 A3 A3 A4 A4 A3 A3 A3 A3 A3 A3 A4 A4 }
// Main melody
melody = { m:8: D4 E4 F4 A4 D5 A4 C5 D5 C5 D5 A4 C5 F4 A4 C5 A4
B4 C5 B4 A4 B4 G4 A4 G4 A4 G4 E4 G4 A4 E4 G4 E4 }
// (Un-)comment lines below to hear different parts and hear the change while
// the music is playing (Make sure to press Ctrl-R to recompile the script.)
texture =
{ bb }
// { hh }
{ oh }
{ sd }
{ bass }
{ chr1 }
{ chr2 }
// "patterns" box has two layers
// -- It plays 4 repetitions of the texture, transposed by an interval from the base tone
// (The global variable %T)
// -- It also plays the "melody" which is 4 bars long
// Remove the // comment to hear it
patterns =
{
%T = 0 texture
%T = 3 texture
%T = 5 texture
%T = 7 texture
}
// { melody }
// This is the main music loop.
// Run this box to hear endless stream of music.
music = { @loop patterns }
 
** GRADRIGO IN UNITY **
-----------------------
 
Gradrigo can be used as a mini-audio middleware in Unity. You may attach different instances
of Gradrigo to different GameObjects in your game. You may have a single instance of Gradrigo
for an entire game or each GameObject can have its own Gradrigo with its own collection
of boxes.
 
In the code of the game you may then instantiate the boxes (even with parameters) and they'll
handle your sound effects.
 
Please see dedicated vidoes on my YouTube channel about the subject:
https://www.youtube.com/channel/UCZt3mPlhyr39hTEQjh_4pRQ
 
** EXPORTING .wav FILES **
--------------------------
 
It is possible to use gradrigo on the command line. One of the command line options is the box
which is supposed to be played and another command is the target .wav file where the output
will be stored.
 
This feature is currently not implemented in this version of Gradrigo but will be very soon.
 
** YOU'RE GOOD TO gradriGO **
-----------------------------
 
This concludes the built-in tutorial. I hope you'll enjoy this mini-engine. Please use
it and when you do and when you publish stuff, please mention Gradrigo in the credits or
description ("Made with Gradrigo")
 
note:%d:%m = {
%d = !(4/%d)
dur(%d) {
fadeout(%d)
@loop
sqr(#%m)
}
}
fanfare* =
{ note: 8: C4 C4 C4 16: E4 G4 8: C5 16: C5 C5 4: C5 }
{ vol(0.5) note: 8: C3 E3 G3 16: C4 D4 8: E4 E4 4: E4 }
{ vol(0.33) note: 4: C2 C2 8: C2 G2 4: C3 }
 
If you plan having splash screens or visible logos somewhere in credits, etc., please
throw in Gradrigo logo as well. It is available for download here:
 
https://gradrigo.com
 
I'll be posting additional pieces of information on the project's landing page as well as on
my social media.
 
https://patreon.com/adam_sporka
https://twitter.com/adam_sporka
https://soundcloud.com/adam_sporka