IMP
)CFG
)MDL
)INS
)OSC
)SEQ
)BLK
)MIX
)SNG
)?
and #
)+
, -
, *
, /
, %
, and ^
)|
)[]
, Set {}
, Series ()
)r
)n
)i
)l
)v
)c
)x
)s
)p
)m
)y
)k
)j
)t
)h
)u
)This is an in-depth guide to the different elements of the Pebble code language. It is set up to be a reference more than a tutorial, so if you are just starting out, you might want to go through the welcome
document first. (This is the first file that loads when you start Pebble.) Or you could try out one of the demo files, by typing demo1
, demo2
, or demo3
into the File bar and pressing Load. You can also enter help
to access this guide in-program, or use ref
to get the quick-reference guide. The default soundpack is sounds
which you can import (IMP sounds
) to access many basic sounds, or you can load it directly to scope out how they are created.
The Pebble code language is modular, which means that all the sounds, instruments, and patterns are created by combining and arranging different modules, each of which typically performs a simple function (like addition, or changing volume).
The next part of this guide will go over the major parts (or 'chunks') of a Pebble song file, and then we will go over each different type of basic module that you can use to make your own custom module.
IMP
)This command loads all Modules, Instruments, Sequences, and Blocks from a given file into the current project. You can either specify an absolute filepath, or Pebble will look in the same folder as the active song. You can also use any of the preloaded files, like sounds
, or welcome
, by simply typing their name.
Usage:
IMP file/path/filename.peb
Each import command must be on its own line, preceded by the IMP
header.
CFG
)This chunk is for setting up Song properties. In the following lines, commands can be used to customize things like tempo and beat length.
Usage:
CFG
STEREO: TRUE
TEMPO: 120
Here are the available commands:
TEMPO
- The tempo of the Song, in beats per minute. Numeric. Default: 120.BEAT
- The number of song steps in 1 beat. Numeric. Default: 4.RATE
- The sample rate for rendered songs. Numeric. Default: 44100. (Note: This does not affect live playback, which is always 32,000Hz.)DEPTH
- The bit depth for rendered songs. Numeric. Default: 16. (Note: This does not affect live playback, which is always 16-bit.)STEREO
- Sets rendered songs to stereo mode. True/False. Default: TRUE. (Note: This does not affect live playback, which is always stereo.)MONO
- Sets rendered songs to mono mode. True/False. Default: FALSE. (Note: This does not affect live playback, which is always stereo.)NORMALIZE
or NORM
- Sets whether to normalize audio in rendered songs to maximum volume without clipping. True/False. Default: TRUE. (Note: This does not affect live playback, which does not normalize audio.MEASURE
- Sets the number of song steps in a measure. Numeric. Default: 16. This is used to determine the length of each space/character in the MIX chunk.These properties are ready top to bottom and can be overwritten later in the file. So to set different properties for different songs, simpl yprecede each SNG
chunk with a CFG
chunk containing the relevant settings. Songs appearing earlier in the file will nto be affected by the updated properties.
MDL
)This chunk allows you to create custom modules for use elsewhere in your project. Each module should be on its own line, beginning with the custom module name.
To use custom modules later on (i.e. in an Instrument, a pitch line, or a Block), simply enter the custom name. (See Naming under Tips & Tricks for more info.
Usage:
MDL
TRI: [0i9,9i0]x*[1,-1]
ARP: [C4,e4,G4,C5]
RAND: {0,2,5,7}+{0,0.5}
About Modules
Almost everything in Pebble is one kind of module or another, which means that many pieces are interchangeable, and you can mix and match quite a bit. Modules can represent a single number, a pattern, or a continuously changing value. Most of the basic module types accept one or more modules as inputs, allowing you to combine any number of simple modules to achieve complex behavior and patterns.
Along with their output, modules also have a length, which tells how long they can be 'played' before stopping. Number value modules have a default length of 1. Basic modules typically calculate their length based on their input modules. Knowing the length of a module can be important for synchronizing patterns to the song, or determining the period of a waveform.
INS
)This chunk allows you to create Instruments, which produce all the sounds in Pebble. Like generic modules, each Instrument has its own line, beginning with the name you'll use for it.
If the Instrument is going to be used directly inside a Sequence, you will need to give it a single-character name. (More on that in the Sequence section.) Otherwise, Instrument names can be as long as you like. (See Naming under Tips & Tricks for more info.)
Usage:
INS
SQR: [9,-9]
SAW: -9i9
A<BASE=SQR,SUS=T>: SQRv[9i2,2i0]
How Instruments Work
Every Instrument has a module representing the sound it makes. When it plays notes, it plays the module at different speeds to achieve the correct pitch. When creating an Instrument, the sound module is the part that comes after the colon. So in the example above, the Instrument SQR
has [9,-9]
as its sound module. This simply jumps between 9 and -9, creating a square or pulse wave.
Note: In Pebble, +/- 9 is considered maximum volume/signal level, so anything above that point may result in clipping and distorted audio.
In order to play sound at the correct pitch, an Instrument needs to know the period of the underlying waveform. You can indicate the period using the PERIOD
or PRD
meta tag, or you can copy the period from another Instrument using the BASE
meta tag (more on these in the 'Meta Properties' section below).
If you do not indicate a period when creating an Instrument, Pebble will default to the full length of the sound module. This will work correctly as long as the sound module describes exactly 1 cycle of the wave.
In the example above, SQR
and SAW
both use the default period value, which will be correct because both of their modules describe 1 wave cycle. In this case, SQR
will have a period of 2, and SAW
will have a period of 1. Instrument A
, on the other hand, uses the BASE
tag to import properties from SQR
. This will set the period of A
to 2 as well, which is good, because the full length of A
is going to be much larger than one wave cycle, since it has an Envelope module that lasts two full song steps.
Instruments can contain other Instruments as part or all of their sound modules. Often it is a good workflow to create the basic waveforms in one Instrument, then use a new Instrument to add things like envelopes and modulation.
Meta Properties for Instruments
There are several meta tags that allow you to customize how an Instrument behaves. To add meta tags, use angle brackets after the Instrument name and before the colon:
INS
TRI<SUS=F,LOOP=T>: [0i9,9i0]x*[1,-1]
Available meta tags:
PERIOD
or PRD
- Sets the period length of the base waveform for this Instrument. If no period length is given, the default will be the full length of the Instrument's sound module. Numeric. Default: Module length.BASE
- Sets the 'base Instrument', which tells Pebble to load all meta properties from the given Instrument into this one. Subsequent meta tags will overwrite imported settings. Set this to the name of an existing Instrument.SUSTAIN
or SUS
- Sets the sustain behavior. If TRUE, the Instrument will play until its sound module has finished. Otherwise, it will stop once the note is lifted in the Sequence. If SUSTAIN
is True, LOOP
will automatically be False. TRUE
/FALSE
. Default: True.LOOP
- Whether the Instrument should automatically loop its sound module until the note has finished playing. TRUE
/FALSE
. Default: True.PAN
- Sets the panning module for the Instrument. Pan values should range between -9 and 9, where -9 is 100% Left and 9 is 100% Right. Set this either to a numeric value or to the name of an existing Module.COPY
- Sets whether to make separate copies of the Instrument for each use (i.e., for separate notes). Can cause unintended effects and strange behaviors if disabled! Use caution. TRUE
/FALSE
. Default: True.OSC
)Oscillators are another kind of generic module that you can use for creating Instruments and effects. The difference between Modules and Oscillators is that Oscillators are run separately by the Song itself, rather than by the parent Module/Instrument/Sequence. So you can apply the same Oscillator to multiple Instruments, and they will always be in sync, even if the notes begin at different times.
Apart from the timing aspect, there is no difference between Oscillators and general Modules, and anything you can put in a Module can be put in an Oscillator. The same syntax also applies, although you will begin the chunk with OSC
instead of MDL
. Afterward, create each Oscillator on its own line, beginning with a name and following it with the module description.
Usage:
OSC
LFO1: c<RATE=0.25>[5i7,7i5]
This example creates an Oscillators called LFO1
, playing it in constant time at 0.25x speed. The oscillator itself will move from 5 to 7 and then back to 5. LFO1
can then be used in any other module, and it will be synchronized with the song itself, rather than the timing of any specific notes or patterns.
SEQ
)Sequences are where you compose music in Pebble. They are written in a form similar to piano roll notation, with multiple lines, each representing a custom pitch (or pitch module).
Each Sequence chunk represents a single Sequence, and a name for the Sequence must be given right after the SEQ
chunk header. The lines beneath the header each have a pitch value and a song pattern, separated by a colon.
In the song pattern, each space/character represents a song step (by default, this is a 16th note, although you can change this in CFG
). Spaces represent rests or silence, letters/symbols refer to Instruments, and hypens (-) represent sustaining notes. (This is why Instruments need to have single-character names to be used in Sequences.)
Additionally, the character '|
' is a measure line. The pattern begins after the first measure line and ends at the last one. All the measure lines in the middle of the pattern are simply ignored, so they can be added wherever for readability purposes.
Usage:
SEQ BEAT1
A5: | A |A A |
G5: |A AA AA| AA AA|
E5: | S--- S--- | S--- S--- |
D3: |B B B B B |B BBB B BB|
In this example, we have a pattern called BEAT1
involving 3 Instruments: A
, B
, and S
. The A
sound plays at pitch G5
and A5
. S
plays longer sustain notes at E5
. And B
plays at D3
.
Sequence Lines
Individual lines in the Sequence begin with a pitch value, which can be:
G6
, E4
, a3
.G 6
, Eb4
, G#3
.C0
Additionally, you can create custom pitch modules directly in this line, to add more complex behavior like arpeggio or portamento. Here's an example:
SEQ ARP
[b4,D5,F5,b5]: |A---------------|
The pitch module is synced to song time, so each pitch in this pattern will last 1 song step. It will loop when finished, so this pattern will play through the full arpeggio 4 times.
Here's another example, with a vibrato effect. Recall that pitches are expressed in cents, so this is +/- 25 cents of vibrato:
SEQ VIB
G4+<LEAD=B>[0i25,25i0,0i-25,-25i0]: |A---------------|
You can also add a panning module to individual Sequence lines using meta tags. In this case, the meta tags must go at the very beginning of the line:
SEQ ARP
<PAN=-3> [b4,D5,F5,b5]: |A---------------|
More About Sequences
SEQ ARP<PAN=-3>
[b4,D5,F5,b5]: |A---------------|
SCALE
property:
SEQ S<SCALE=8>
G4: |A---|
SCALE
to 8, meaning each space is equal to 8 steps. So even though the pattern is only 4 spaces long, the Sequence will be 32 song steps (2 measures).If you don't want to do that, you can use PITCHLOCK
(also PTLK
) and PANLOCK
(also PNLK
) properties to "lock" these modules so that they only run when an active note is playing on the line.
SEQ A
<PITCHLOCK=T> [C4,G4]: |M M |
D4: | M M |
Here we are turning PITCHLOCK
on for the top line, so the pitch pattern [C4, G4]
will only advance while a note is playing. This means our first note will be a C4
, and our second note will be a G4
, even though they are spread apart. (Without locking the pitch, both of these notes would play as C4
because of their timing.)
You can also turn on PITCHLOCK
for a whole Sequence:
SEQ A<PITCHLOCK=T>
[C4,G4]: |M M | D4: | M M |
PANLOCK
works the same way, but for the panning module.
BLK
)Blocks allow you to combine and manipulate Sequences in the same way you would any other module. Accordingly, the format of the BLK
chunk is similar to the MDL
and INS
chunks, where multiple blocks can be created, each on their own line. Every line begins with a custom name, followed by a colon, and then the Block description.
The resulting Blocks are stored with the other Sequences, so they can also be used in the song, or even in other Blocks.
Usage:
BLK
PT1: [BT1r2,BT2,BT1]
ARP: {ARP1,ARP2}
LEAD<PAN=2>: LDl6
More about Blocks
Like Sequences, Blocks can also have a custom panning module. This is indicated by a meta tag, which comes after the name in the Block description (like with Instruments). In the example above, LEAD
has a custom pan value of 2.
MIX
)The Mix chunk lets you arrange song patterns in the same way you arrange notes in the Sequence chunk. Each line is a separate Track, which has a Volume module and a pattern that can contain one or more Sequence modules or Blocks. (As with the Instruments in Sequences, these modules need to have single-character names to be used in the MIX chunk.)
Each Mix chunk represents a single Mix, and a name for the Mix must immediately follow the MIX
chunk header. The lines below each begin with a volume module (0
to 9
, or a Module name), followed by a colon and then the Track pattern.
The Track pattern must begin and end with a measure line (|
), and every space or character in the pattern represents an entire MEASURE
. Other measure lines are ignored by the parser, so you can use them as you like to make your Mix more readable.
Usage/Example:
MIX EXAMPLE
9: |A---B---A---B---|
8: | RSRSRSTTRSTT|
4: | C- C- C- C-|
In this Mix we have 3 Tracks, set at volumes 9, 8, and 4. In the first Track, we alternate between playing A
and B
every 4 measures. (The hyphens tell the Track to continue playing, just like with sustain notes in Sequences.) In the second Track, we have 4 measures of silence, and then we begin a pattern using R
, S
, and T
. And the final Track has 2 measures of silence, followed by 2 measures of C
.
By default, every space in the Track pattern represents a Measure of the song, which can be customized using the MEASURE
property in the Config chunk. However, you can change this for the Mix itself using the SCALE
property, which specifies the number of song steps per space:
MIX MN<SCALE=4>
7: |A--- B-------|
5: |C-C-D---C-C-D---|
5: | E---------F-|
After you create a Mix, it works just like a Sequence or a Block. You can use them directly in the Song pattern, or you can use them in a Block or even another Mix!
Meta Properties for Mixes and Tracks
You can customize the behavior of mixes with several different properties, which can either be applied to individual Tracks, or the whole Mix. Like with Sequences, include Mix-level properties right after the Mix name, and Track-specific properties at the beginning of the Track line:
MIX MN<LOOPSEQS=F> // Mix-level property, turning off Sequence looping
7: |A----- |
<LEVELLOCK=T> [5,8]: |B B C-| // Track-level property turning on level-lock
Available meta tags:
LEVELLOCK
or LVLK
- Locks the Track's level/volume module to only run while a Sequence is actively playing on the Track. True/False. Default: False.PAN
- Sets the Track's panning module. (Not available for the whole Mix.) Numeric or Module name. Default: 0 (Centered).PANLOCK
or PNLK
- Locks the Track's Pan module to only run while a Sequence is actively playing on the Track. True/False. Default: False.LOOPSEQS
or LPSQ
- Automatically loops Sequences that finish while still sustaining. True/False. Default: True.SUSSEQS
or SUSQ
- Allows sustaining notes in a Sequence to continue until finished, even if the pattern is not sustaining them. True/False. Default: True.SCALE
- The number of song steps covered by each space/character in the Mix pattern. By default, this will use the Song's MEASURE
property (normally 16). Numeric. Default: 16.COPY
- Whether each use of the Mix should be its own separate copy. True/False. Default: True. (Warning: Disabling this can have strange and unintended effects. Use caution!)SNG
)The Song chunk is where you tell Pebble what to actually play/render. A Song is simply a series of Sequence modules and Blocks which will be played one after the other.
Song chunks can either be all on one line or split over multiple lines, so:
SNG A,B,C
Is the same as:
SNG A
B
C
Modules on the same line should be separated by commas.
You can also write out custom modules in the Song chunk, much as you would when creating Blocks:
SNG A, A+B, [A+B,C]r2, C
By default, Pebble Songs will get their name from the name of the source file. However, it is also possible to set custom names for individual Songs. To do this, simply enter the name after the SNG
chunk header, then put a colon, followed by the song description. Without the colon, Pebble will defer to the default name.
Example:
SNG track1: A, B, C
This will get the custom name track1
, so it will render to track1.wav, instead of [filename].wav.
Pebble files can have multiple Songs. In live playback mode, Songs will play one after the other. In Render mode, all Songs will be rendered individually.
Note: If you are rendering multiple songs from a single file, you MUST specify custom names for each Song. Otherwise, they will all be saved to the default filename, causing each Song to overwrite the previous one!
Almost everything in Pebble is a module, and most modules are interchangeable, which gives you lots of room to create & explore.
Pebble offers a number of basic modules, from which you can build all sorts of custom modules. Many of these basic modules take one or more modules as inputs, and then apply some sort of operation or effect to them.
This part of the guide will cover each basic module type in detail. For more information about where and how to create custom modules, see the Module (MDL) chunk category. Instrument, Block, and Song chunks also use this custom module format, as well as the pitch portion of Sequence lines.
Value modules simply represent a fixed numeric value. Most of the time when you enter a number in Pebble, you are creating a Value module. Values can be positive or negative, integer or decimal.
Usage:
MDL
VAL1: 9
VAL2: -9
VAL3: 0.125
Technically, all Values represent a stereo pair, meaning they have a value for both Left and Right channels. If you enter a single number, both L and R will be set to this amount. However, if you want to set Left and Right independently, you can use the |
symbol (see LRJoin below):
MDL
VAL: (4.5|9)
By default, the length of a Value module is 1. However, you can set custom lengths using the LEN
meta tag:
MDL
VAL: 9<LEN=2>
?
and #
)There are two modules that generate random numbers in Pebble: Random (?
), and Random Value (#
).
The Random module outputs a random number between 0 and 1, which you can adjust as needed using the math operators.
The Random Value module outputs a random number between -9 and 9, which makes it a simple noise wave generator. You can also use the Level module or another operation to adjust it.
By default, both random number modules are mono, meaning they output the same value on the Left and Right channels. If you want, you can use the STEREO
meta tag to change this:
MDL
RNDSTEREO: #<STEREO=T>
+
, -
, *
, /
, %
, and ^
)You can perform basic math operations on modules using their regular symbols:
+
-
*
/
%
^
Each of these creates a new module which performs the given operation on its two input modules.
Example:
MDL
ADD: 1+2
This will create a new module called ADD
that will output 3. When you use a math operator on two static values (like in this case), Pebble will automatically reduce the result into a simple Value module, to improve performance.
Math operator modules determine their length from one of their input modules, called the lead. By default, the lead is input A
, which is the first one listed. You can set the lead module to input B
with the meta tag LEAD
:
MDL
ADD: FRST+<LEAD=B>SCND
This sets the lead to module SCND
, because it is the second one listed (or module 'B').
When a math operator module is reset, only the lead module will be reset (because it is the one that has stopped). The other module will be reset independently, whenever it stops. You can use the RESET
or RST
meta property to tell the math operator to reset both modules like so:
MDL
ADD: FIRST<RESET=BOTH>SCND
|
)This module combines 2 inputs, using the first for the Left channel and the second for the Right. Only the corresponding side will be used for each input module (so a sound panned Right on the Left input will be omitted).
[]
, Set {}
, Series ()
)Grouping modules let you combine one or more other modules. There are 3 types of group, each with different behaviors:
[]
Patterns play modules one after the other. The pattern module will continue playing until its last module is done.
Example:
MDL
The length of a Pattern module is the sum of the lengths of all its elements.
()
A Series is similar to a Pattern, except it plays modules one at a time. Every time the current module is done, the Series also stops. When it is reset, it will advance to the next module, and next time it will play through that.
Series are good for adding specific variations into a repeated pattern or phrase.
Example:
MDL
SRS: (G4,A4,C5,A4)
MLDY: [C4,SRS]r4
In this, the MLDY
module will alternate C4
with the notes of the series SRS
: C4
, G4
, C4
, A4
, C4
, C5
, C4
, A4
.
The length of a Series module is inherited from the length of its active element, meaning Series may not always report its length consistently if it has elements of differing lengths. (This isn't usually a problem, but could cause some unintended behavior.)
{}
A Set will randomly select one of its modules to play each time it is reset. When the currently selected module stops, the Set will also stop, and a new module will be randomly selected when the Set is reset.
Sets are a good way of adding randomization/variation into a song, or adding some noise to a sound wave.
Example:
MDL
SET: {0,1,-1}
The length of a Set module is inherited from the length of its active element, meaning Set may not always report its length consistently if it has elements of different lengths. (This isn't usually a problem, but could cause some unintended behavior.)
You can tell a Set to choose a certain one of its elements using the INDEX
(or INX
) property. This will override the random behavior. This should be a number, with 0 representing the first element in the Set.
You can also tell a Set not to choose a new item after being reset by setting the STATIC
property to TRUE
(or T
). This is good for when you want Set to make a random choice, but you don't want that choice to change in the middle of the Song.
More About Groupings
If you create a group that only has 1 element, Pebble will automatically reduce it to the element module, to improve performance. This allows you to also use parentheses or square brackets to separate out specific modules that would otherwise be parsed incorrectly, without worrying about bogging things down with lots of unnecessary groups.
r
)The Repeat module repeats its input module a certain number of times before stopping.
Usage:
MDL
RPT: ArB
Where A
is the module to be repeated, and B
is the number of times to repeat.
To make the module repeat indefinitely, set the repeat value to -1
(but be sure to have some other way of stopping it further up!).
n
)The Length module overrides the length of its input module.
If the new length is greater, the input module will be reset until the new length is reached. If it is shorter, the input module will be stopped early.
Usage:
MDL
LEN: AnB
Where A
is the input module, and B
is the length controller.
i
)The Interpolation module transitions between two values using linear interpolation. This happens over a fixed width, which can be set using meta tags.
Interpolation allows you to create smoother transitions between values, eliminating clicks and reducing noise.
Usage:
MDL
INT: AiB
In this case, the module will start at value A
and interpolate to B
.
The length of an Interpolation module is equal to its width value. By default the width is 1, but this can be set using the WIDTH
or W
meta tags:
MDL
LIN: 0i<WIDTH=4>5
This transitions from 0 to 5, over a width of 4.
Helpful Trick
You can nest Interpolation modules to create smoother transitions, allowing you to create sine-like waveforms and smooth (quadratic or cubic) curves. Here is an example of how that looks:
INS
SIN: [(0i9)i9,9i(9i0),(0i-9)i-9,-9i(-9i0)]
The first module, (0i9)i9
, creates a convex curve going up from 0 to 9. The second, 9i(9i0)
, creates a convex curve going down from 9 to 0. The next two simply repeat these, but approaching -9 instead.
If you wanted to do a concave curve, you would switch the order: 0i(0i9)
.
You can also interpolate from concave to convex, to get even smoother transitions:
MDL
CUBIC: (0i(0i9))i((0i9)i9)
It can be tricky to figure out how to set it up to get the correct behavior, but it might help to picture each combination of operators. In the concave example, 0i(0i9)
, we start was though it were 0i0
--0 approaching 0, so a straight line at 0. But as we go along, we are approaching 0i9
, which is a diagonal line from 0 to 9. So you can imagine the line of motion slowly turning from horizontal to diagonal, giving us our concave curve from 0 to 9.
(Note: This isn't technically a sine wave, but I tend to call them 'sine'. It's more of just a curved wave, like Bézier curves. They look and sound similarly, though.)
l
)The Level module adjusts the volume of its input module. Volume in Pebble goes from 0 to 9, with 9 as the default. So a volume of 9 will return the input signal at its original level, and lower values will reduce volume. Volume 0 returns silence.
(It may help to imagine a fader on a mixer, with 9 set at the 0dB mark and 0 at the -infinity dB mark!).Usage:
MDL
LVL: AlB
Where A
is the input module, and B
is the volume level controller.
By default, Level inherits its length from the input module (A
). If you want to switch this to the level controller module, you can set the LEAD
meta tag to B
:
MDL
LVL: INPTl<LEAD=B>5
When a Level module is reset, only the lead module will be reset (because it is the one that has stopped). The other module will be reset independently, whenever it stops. You can use the RESET
or RST
meta property to tell the operator to reset both modules like so:
MDL
LVL: Al<RESET=BOTH>B
v
)Like Level, Envelope adjusts the volume of its input module. However, Envelope has additional behaviors tailored to creating volume envelopes for sounds:
RATE
value, which sets the rate at which they play through their volume module.Usage:
MDL
ENV: AvB
Where A
is the input module, and B
is the volume control module.
Meta Tags:
RATE
or R
- Sets the playback rate of the volume envelope. A playback rate of 2 will play the envelope twice as fast, where 0.5 will be half as fast. The input module is not affected. Default: 1.LOOP
or L
- If True, the Envelope will reset its volume module once it is finished. In this case, the Envelope must be stopped externally. Default: False.ATK
- The Attack point for looping. When the volume module resets, it will be advanced to this point. Default: 0.REL
- The Release point for looping. If the Envelope has not received an Instrument release signal, it will not advance the volume module past this point, and it will loop instead. Once a release signal is received, it will play through until the end. A value of -1 indicates that no release point is set, and this behavior will not occur. Default: -1.Examples:
INS
A<BASE=TRI,SUS=T>: TRIv[9i<WIDTH=2>0]
This is an Instrument using a very simple Envelope. The volume starts at 9 (full volume), then reduces to 0. This will take 2 song steps, since the WIDTH
value of the Interpolate module is set to 2.
INS
B<BASE=TRI>: TRIv<RATE=4,LOOP=T>[9i7,7i9]
This Envelope is set to loop, so it will reduce volume from 9 to 7, then increase it back up to 9, repeating as long as the Instrument sustains. Because the RATE
tag is set to 4, this will happen at 4x song speed, completing the pattern twice every song step.
INS
C<BASE=TRI,SUS=T>: TRIv<ATK=2,REL=3>[6i9,9i7,7,7i0]
In this example, we have set the Attack and Release points of the Envelope to create a more dynamic loop. Initially, the sound will spike up from 6 to 9, then back down to 7. It will level off at 7 for one step, at which point we will hit the Release Point (3). If the Instrument is not yet released, it will loop back to the Attack Point (2), which is the beginning of the constant 7 value. Once the Instrument releases, it will play through to the 7i0
, which fades out to silence and stops the sound.
c
)The Constant-Time module forces a module to run in constant/song time. This synchronizes it to song steps, so a length of 1 will cover 1 song step, and so on. When used in an Instrument, the Constant-Time module will not be affected by pitch. This makes it helpful for creating parameter envelopes and LFO effects.
Usage:
MDL
CNS: cA
Where A
is the module to run in constant time.
Additionally, Constant-Time has several meta tag properties that can be used to set up looping behavior and playback rate.
Meta Tags:
RATE
or R
- Sets the playback rate. A rate of 2 will play the module twice as fast, while 0.5 will play half as fast. Default: 1.LOOP
or L
- Sets whether to loop the input module when it stops. If this is True, the module will need to be stopped externally, unless a Release point has been set. Default: False.ATK
- Sets the Attack point for looping. When the module resets, it will loop back to this point. Default: 0.REL
- Sets the Release point for looping. If the module has not received an Instrument release signal, it will not advance past this point, looping instead. Once a release signal is received, it will play through until the end. A value of -1 indicates that no release point is set, and this behavior will not occur. Default: -1.x
)The Cross module can be applied to many two-input operators to change how they behave. Normally, operations (like the Math Operators, or Level), run both of their input modules at the same speed during playback. However, with Cross, the second module speed is adjusted so that the first module runs through its entire length for each step of the second module.
Usage:
MDL
CRS: Ax+B
Where A
and B
are the two inputs for the original operation (in this case is addition).
As a quick way of paraphrasing: Adding a Cross module to an A+B
module will tell it to add all of A to each of B.
Here's an example of how that looks:
MDL
A: [0,1,2]
B: [0,1]
ADD: A+B
XAD: Ax+B
The outputs of the following modules will be:
ADD: [0+0, 1+1, 2+0] = [0,2,2]
XAD: [0+0, 1+0, 2+0, 0+1, 1+1, 2+1] = [0,1,2, 1,2,3]
s
)The Speed module changes the playback rate of an input module. This affects both dynamic and constant time, so pitch will be affected when this is applied to an Instrument. Currently, pitch is NOT affected when this is applied to Sequences/Blocks, so it only affects tempo in that case.
Usage:
MDL
AsB
Where A
is the input module, and B
is the speed controller.
By default, Speed inherits its length from the input module (A
). If you want to switch this to the speed controller module, you can set the LEAD
meta tag to B
:
MDL
SPD: INPTs<LEAD=B>2
When a Speed module is reset, only the lead module will be reset (because it is the one that has stopped). The other module will be reset independently, whenever it stops. You can use the RESET
or RST
meta property to tell the operator to reset both modules like so:
MDL
SPD: As<RESET=BOTH>B
p
)The Pitch module transposes pitch values for any Instruments within a module. This can be applied to Instruments, Sequences, and Blocks.
Usage:
MDL
PTC: ApB
Where A
is the input module, and B
is the pitch controller.
Pitch transposition is expressed in cents (100 per semitone). So Ap200
will raise A
by a whole step (200 cents), and Bp-500
will lower B
by a fourth.
By default, Pitch inherits its length from the input module (A
). If you want to switch this to the pitch controller module, you can set the LEAD
meta tag to B
:
MDL
PTC: INPTp<LEAD=B>200
When a Pitch module is reset, only the lead module will be reset (because it is the one that has stopped). The other module will be reset independently, whenever it stops. You can use the RESET
or RST
meta property to tell the operator to reset both modules like so:
MDL
SPD: Ap<RESET=BOTH>B
m
)The Limit module limits an input module to a certain signal level.
Usage:
MDL
LMT: AmB
Where A
is the input module, and B
is the volume limit.
If the absolute value of the input is greater than the absolute value of the limit, the input value will be clipped to the limit value (while retaining its original sign).
You can also have the Limit module auto-adjust the gain back to full volume by setting the MAKEUP
(or MK
) property to TRUE
(or T
). This adjust the volume based on the inverse of the clip level, so that clipped values will be scaled up back to 9 (or -9).
Additionally, Limit has an optional KNEE
property which will soften a certain amount of the signal beneath the limit, creating a smoother clipping. When KNEE
is not equal to 0, the smoothing formula replaces clipping.
Example:
MDL
KNE: Am<KNEE=2>8
In this example, the limit amount is 8
and the knee is 2
, so the input signal will be smoothed once it is outside the range -6 to 6, and it will approach a maximum output value of +/-8.
By default, Limit inherits its length from the input module (A
). If you want to switch this to the limit controller module, you can set the LEAD
meta tag to B
:
MDL
LMT: INPTm<LEAD=B>9
When a Limit module is reset, only the lead module will be reset (because it is the one that has stopped). The other module will be reset independently, whenever it stops. You can use the RESET
or RST
meta property to tell the operator to reset both modules like so:
MDL
LMT: Am<RESET=BOTH>B
y
)The Delay module adds a typical delay effect to the input module. Original signal is played back delayed by a certain number of song steps.
Usage:
MDL
DLY: AyB
Where A
is the input module, and B
is the delay amount (in song steps).
The delay amount module can be variable, but this may cause performance issues, if Pebble has to increase the delay buffer size too frequently.
Delay offers meta properties for further controlling the behavior of the effect.
Meta Tags:
WET
- The amount of wet (delayed) signal to send (0
is none; 1
is 100%). Default: 1.DRY
- The amount of dry (input) signal to send (0
is none; 1
is 100%). Default: 1.FEEDBACK
, FDBK
, or FD
- The amount of wet signal to feed back into the delay buffer (0
is none; 1
is 100%). WARNING: 100% feedback will create an infinite loop, and the module will not stop on its own. Default: 0.MAX
- Sets the maximum delay amount. If you're going to change the delay time mid-song, it's a good idea to set this to whatever the longest time will be. This allows Pebble to avoid having to resize the delay buffer mid-playback, which can cause lag & audio glitches. Numeric. Default: no maximum.k
)The Attack module sets an attack looping point for an input module. When the module is reset, it will return to the attack point, instead of the zero position like normal.
Usage:
MDL
ATK: AkB
Where A
is the input module, and B
is the attack point controller.
j
)The Release module sets a release looping point for an input module. When the module reaches this point, it will reset, unless it has already received an Instrument release signal. This is sent by Sequence Lines when the original note is finished sustaining.
Once the release signal has been received, the module will play through to the end and then stop.
Usage:
MDL
REL: AjB
Where A
is the input module, and B
is the release point controller.
t
)The Absolute Value module returns the absolute value of an input module.
Usage:
MDL
ABS: tA
Where A
is the input module.
h
)This module pans the given input left or right. Panning goes from -9 (100% Left) to 9 (100% Right), with 0 being Center.
Usage:
MDL
PN: AhB
Where A
is the input module and B
is the amount of panning.
u
)Applies a portamento (pitch slide) effect to the input module. This causes all pitch changes to be applied gradually over a given number of song steps.
Usage:
INS
PTA: AuB
Where A
is the input module and B
is the portamento time (in song steps).
Note: Different notes in a Sequence pattern are each different copies of the same Instrument, so they will not receive pitch changes to apply portamento to. You can either have one sustaining note with changes in the line's pitch module, or use the Pitch operator to change the instrument's pitch.
Alternately, you can set the Instrument's COPY
property to FALSE
, although this can have some unintended effects. (For example, it will glitch up if you play more than one note on that instrument at the same time, including sustained notes.) See more about COPY
in the next section.
COPY
Behavior
By default, Modules will be copied every time they are used, so they can run independently without interfering with each other. (For example, if you play two notes on an instrument at the same time, they will probably need to have different pitches!)
However, if you want, you can disable this behavior using the COPY
property:
MDL
NC<COPY=F>: [0i9,9i0]
This will tell the module not to make copies of itself, so only one version of the module will exist in the song. This can have a lot of unintended effects, but it could be useful in some situations where you don't want things to fully reset between separate notes / patterns / etc. Use with caution!
Use capital letters when naming modules in Pebble, as lower-case letters are reserved for modules. You can use numbers, but your module name cannot start with a number. You can also use any keyboard symbol that is not currently used for another operation.
Reserved Symbols: +-*/%^=|,[]{}()<>#
If you are using an Instrument in a Sequence, it will need to have a single-letter name. However, giving all Instruments single-letter names can make it difficult to keep them organized. A good method is to use longer, more descriptive names while you're creating the Instrument, then assigning it to a single-letter Instrument just before using it in the Sequence. Here's an example:
INS
SQR: [9,-9]
LEAD<BASE=SQR,SUS=T>: SQRv<ATK=1,REL=2>[4i9,9,9i4,4i0]
A<BASE=LEAD>: LEADl5
SEQ LDA
G4: | A-| A--- A-| |A--- A-|
F4: | |A--- | A- | A- |
E4: | A- | A--- |A----------- A| A- A--- |
D4: |A----------- | A- | A | A- |
In this example we create a basic waveform, SQR
and give it an envelope in LEAD
. Then we assign our LEAD
Instrument to A
, which is what we use in our Sequence LDA
.
We can reuse A
for another instrument later in the file simply by reassigning it, and LDA
will not be affected.
Pebble Instruments play sounds by cycling through a waveform module at different speeds according to the pitch being played.
If you've used synthesizers or worked with electronic music before, you're probably familiar with basic waveforms like saw, sine, pulse, triangle, and noise waves. In Pebble, you can create all of these (or similar), as well as entirely custom waveforms.
The sound space in Pebble ranges from -9 to +9. Values outside of this range may cause clipping or distortion.
Typically, waveforms in Pebble oscillate between +9 and -9 (or visa versa). The time it takes for a waveform module to complete one cycle of this is its period. Instruments need to know the period of their base waveform in order to create the correct pitch. You can set the period values for an Instrument using the PERIOD
or PRD
meta tag. Instruments will also import period values from their BASE
Instrument, if that meta tag is used.
If the Instrument module is a simple waveform and it only describes one cycle of the wave, you do not need to specify the period. Pebble Instruments will set their period to the module length by default when no period is given.
A good workflow is to begin with a simple waveform Instrument, then use that as a base for more complex features, like Envelopes and effects. Let's look at an example:
INS
// a simple triangle wave, for our base
TRI: [0i9,9i0,0i-9,-9i0]
// no need to specify the period, because this describes 1 wave cycle
// adding a pitch effect, for vibrato
VBO<BASE=TRI>: TRIpc<RATE=8>[0i10,10i0,0i-10,-10i0]
// +/- 10 cents of vibrato, played at a constant rate, 8x speed
// adding a volume envelope
TONE<BASE=VBO, SUS=T>: VBOv<RATE=2,ATK=1,REL=2>[9i7,7,7i0]
In this example, our base waveform is TRI
. The module [0i9,9i0,0i-9,-9i0]
is 4 steps long and describes 1 full cycle of the wave (going up to 9, down to -9, then back to 0). If we did want to specify the period, we could have done:
TRI<PRD=4>: [0i9,9i0,0i-9,-9i0]
Which would have the same result.
In VBO
, we added a pitch envelope, using TRI
as our BASE
, which imports the period value and other settings from TRI
. Then in TONE
we added a volume envelope, using VBO
as a base. In this one, we also set SUS=T
, which turns on the sustain property, allowing the Envelope to control stopping behavior.
Pebble will try to run as much as you ask it to, but at some point if it can't keep up, you might experience some audio glitches, lagging, or skipping during live playback. You can still render these songs without problems, but you'll need to reduce the load on your computer for it to play live in the editor.
Here are a few tips for improving performance:
One good way to improve the performance of Delay effects is to apply them at the Sequence level, instead of adding delay to individual Instruments. For example: If you have 8 notes in a pattern with Delay on the Instrument, Pebble will run all 8 sounds until the Delay is complete. However, if you add Delay to that whole Sequence, Pebble will only have to run 1 Delay module until it finishes. The individual instruments will have already finished, and all their sounds will be replayed through the Sequence's Delay module. The resulting effect is the same, but the performance is much better.
Note: I've really only had problems with this when making very complicated Instruments, typically that contain 3+ other Instruments nested inside them.
Pebble automatically simplifies modules when it parses them, so you do not need to worry about things like:
(1/3)
, Pebble will automatically reduce this to a simple value of 0.333..., instead of calculating 1/3 every frame.