Pebble

Reference Guide

itch.ioPebble

Table of Contents

  1. Overview

  2. Chunks

    1. Import (IMP)

    2. Config (CFG)

    3. Module (MDL)

    4. Instrument (INS)

    5. Oscillator (OSC)

    6. Sequence (SEQ)

    7. Block (BLK)

    8. Mix (MIX)

    9. Song (SNG)

  3. Modules

    1. Overview

    2. Values

    3. Random Numbers (? and #)

    4. Math Operators (+, -, *, /, %, and ^)

    5. LRJoin (|)

    6. Groups (Pattern [], Set {}, Series ())

    7. Repeat (r)

    8. Length (n)

    9. Linear Interpolation (i)

    10. Level (l)

    11. Envelope (v)

    12. Constant-Time (c)

    13. Cross (x)

    14. Speed (s)

    15. Pitch (p)

    16. Limit (m)

    17. Delay (y)

    18. Attack (k)

    19. Release (j)

    20. Absolute Value (t)

    21. Pan (h)

    22. Portamento (u)

  4. Tips & Tricks

    1. Naming

    2. Creating Sounds

    3. Performance & Optimization


Overview

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.


Chunks

  1. Import (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.


  1. Config (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 32000Hz.)

  • 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 read from top to bottom and can be overwritten later in the file. So to set different properties for different songs, simply precede each SNG chunk with a CFG chunk containing the relevant settings. Songs appearing earlier in the file will not be affected by the updated properties.


  1. Module (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.


  1. Instrument (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).

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: False.

  • 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 if disabled! Use caution. TRUE/FALSE. Default: True.


  1. Oscillator (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 Oscillator called LFO1, playing 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.


  1. Sequence (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:

  • A 2-letter pitch (note letter + octave; lower-case letters are flats), Examples: G6, E4, a3.

  • A 3-letter pitch (note letter + [#,b, or space] + octave), Examples: G 6, Eb4, G#3.

  • An existing module containing these pitches.

  • A numeric value (in cents above C0)

Additionally, you can create custom pitch modules directly in the 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 be 1 song step. It will loop when finished, so this pattern will play through the 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

  • The overall length of a Sequence is determined by its longest line. Each line will start at the same time, and any that finish early will simply be silent for the rest of the duration.

  • Like the individual lines, Sequences can also have their own panning module, which applies to all the lines. In this case, the meta tag comes after the Sequence name:

SEQ ARP<PAN=-3>
[b4,D5,F5,B5]: |A---------------|

  • Both Sequences and Sequence Lines accept custom modules for panning.

  • By default, each space in a Sequence pattern represents a single song step, but you can change this using the Sequence's SCALE property:

SEQ S<SCALE=8>

G4: |A---|

In this example, we set 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).

  • Normally, pitch and pan modules in a Sequence Line will run any time the Sequence is running, even when no Instrument is playing on their line. This can make it a little tricky to line up things like pitch changes, because you might have to account for silence between notes.

If you don't want to do that, you can use the PITCHLOCK (also PTLK) and PANLOCK (also PNLK) properties to "lock" these modules in sync with only actively-playing notes:

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 will 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.


  1. Block (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.


  1. Mix (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 using the Mix's 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!)


  1. Song (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!


Modules

  1. Overview

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.


  1. Values

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>


  1. Random Numbers (? 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>


  1. Math Operators (+, -, *, /, %, ^)

You can perform basic math operations on modules using their regular symbols:

  • Add: +

  • Subtract: -

  • Multiply: *

  • Divide: /

  • Modulus: %

  • Exponent: ^

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


  1. LRJoin (|)

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).

  1. Groups (Pattern [], Set {}, Series ())

Grouping modules let you combine one or more other modules. There are 3 types of groups, each with different behaviors:

  • Pattern - []

Patterns play modules one after the other. The pattern module will continue playing until its last module is done.

Example:

MDL
PAT: [0,1,2,3,4]

The length of a Pattern module is the sum of the lengths of all its elements.

  • Series - ()

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.)

  • Set - {}

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.


  1. Repeat (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!).


  1. Length (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.


  1. Interpolation (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 tag:

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.)


  1. Level (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.

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


  1. Envelope (v)

Like Level, Envelope adjusts the volume of its input module. However, Envelope has additional behaviors tailored to creating volume envelopes for sounds:

  • In Envelopes, the volume control module runs in song time. That means if the length of the volume module is 4, it will run for 4 song steps.

  • Envelopes have optional properties for looping, attack, and release.

  • Envelopes have a customizable 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 it 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 (max), then reduces to 0. This will take 2 song steps, since the width value of the Interpolate 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,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 marker (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.


  1. Constant-Time (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.


  1. Cross (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 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]


  1. Speed (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


  1. Pitch (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: A
p<RESET=BOTH>B


  1. Limit (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 limit threshold.

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

  1. Delay (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. 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.


  1. Attack (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.


  1. Release (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.


  1. Absolute Value (t)

The Absolute Value module returns the absolute value of an input module.

Usage:

MDL
ABS: tA

Where A is the input module.


  1. Pan (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.


  1. Portamento (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 difference 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 below.


  1. More about Modules

  • 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!


Tips & Tricks

  1. Naming

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 the 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.


  1. Creating Sounds

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.


  1. Performance & Optimization

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:

  • Check on long-sustaining notes. If you have a lot of sustain notes playing in a short time, so that they overlap quite a bit, this can add up quickly, as each Instrument must continue playing until its module is complete.

  • Check on Delay effects. Delay is in the same boat as sustain, and if Delay is set to have feedback, the module will continue until the wet signal has fallen mostly silent.
    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.

  • Consider simplifying Instruments. You can make some excellent sounds by layering and modulating Instruments, but this also increases the demands on the computer. If you start to notice problems, you might swap out some of your high-performance instruments for something simpler during live playback, then switch them back when you are ready to render. Comments are a good way to do this!
    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:

  • Static-value calculations. If you have a module that uses (1/3), Pebble will automatically reduce this to a simple value of 0.333..., instead of calculating 1/3 every frame.

  • Single-element groups. If you use parentheses or brackets to separate out a module, Pebble will parse this as just the contents of the group, rather than creating a Series or Pattern with only one element.

  • Reassigning instruments. If you create an Instrument that contains another Instrument (without any manipulations), the new Instrument will instead copy the old Instrument's sound module, to avoid unnecessarily nesting the Instruments (and duplicating their behaviors every frame). If you set the new Instrument's BASE tag to the old Instrument, you are essentially creating a simply copy of the old one under a new name.