HISE Docs

HiseScript Coding Standards

Introduction

The goal of this document is to provide a set of rules that can be followed to produce clean and readable HISE script. Through the use of such a framework developers can share code and have a high level of confidence that others will be able to read, understand, modify, and maintain it without too much difficulty. Although some elements of this guide aim to encourage better coding practices, following these guidelines will not guarantee that your code is bug free or efficient, but it will be readable.

HISE script is born from a mixture of Javascript and C++; This guide tries to follow pre-existing conventions for those languages with particular inspiration taken from the JUCE Coding Standards , LLVM Coding Standards , and the Airbnb Javascript style guide . Other elements, unique to HISE, are based upon Christoph's blogs, documentation, and forum posts, and the default behaviour of the HISE script editor when possible.

General Formatting

Source Headers

At the top of every source file there should be a header section that includes a copyright notice and the license under which the source code is published. This is especially important when using a free license such as the GNU GPL or MIT.

/*
* --------------------------------------------------------------------------
* 
* Copyright  

* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions...
*
* -------------------------------------------------------------------------- 
*/

Comments

// Bad
/*
 */

// Good
/*
*/

Line length

const x = 1 + 12
          * 100 - 50;

White space

//Bad 
inline function doSomething()
{
  
  Console.print("FooBar");
  
}

//Good
inline function doSomething()
{  
  Console.print("FooBar");  
}
// Bad
if(x == 1)

// Good
if (x == 1)
// Bad 
const fish = [ "Shark", "Gold", "Trout", "Salmon" ];

// Good
const fish = ["Shark", "Gold", "Trout", "Salmon"];

Statements

function myFunction()
{
}
// Bad
if (x == 1)
{
  doSomething();
}

// Good
if (x == 1)
  doSomething();

If

Either all of them use braces, or none of them use braces.

if (x == 1) return "one";
if (x == 2) return "two";
if (x == 3) return "three";

Ternary

If it spreads over multiple lines use an if-else instead.

// Bad
const foo = a ? false : true;

// Good
const foo = !a;

Switch

switch (value)
{
    case 0:
        doSomething();
        break;
}
switch (value) 
{
    case 0:
    {
        doSomething();
        break;
    }

    case 1: 
    {
        doSomethingElse();
        break;
    }
}

Cases don't have their own scope. If you declare a variable within a case statement and that case is reached, the variable will be available outside of the switch statement too. Declare the variable outside of the switch statement so its scope is clear from the start.

// Bad
const c = 1;

switch (c)
{        
    case 1:
        var pizza = "Cheese";
        Console.print(pizza);
        break;
}
    
Console.print(pizza);

// Good
const c = 1;
const pizza = "Cheese";

switch (c)
{        
    case 1:
        Console.print(pizza);
        break;
}
    
Console.print(pizza);
const c = 1;

switch (c)
{        
    case 1:
        Console.print("Hello");
    
    case c > 0: 
        Console.print(" World");
        break;
}
const c = 10;

switch (c)
{        
    case 1:
        Console.print("First Case");
        break;
    
    default: 
        Console.print("Default!!!");
}

Loops

Use for loops when you need access to the current iteration, e.g. i .

This is because by default iterators exists in the global scope, declaring the function's iterator as local prevents it conflicting with the global version.

for (i = 0; i < 10; i++)
    Console.print(i + getSecondValue());
    
inline function getSecondValue()
{
    local i;

    for (i = 10; i < 20; i++)
        return i;
}

When using nested for...in loops use appropriate variable names based on the names of the objects being looped, following the rule above when possible.

for (x in modulators)
  return x.get("id");

// Or
for (m in modulators)
  return m.get("id");

Variables



> When there are many variables or they have longer names it is better to use a namespace or object.

Naming

This improves readability as such variables will clearly standout.

// Bad
global myVariable = 1;
global myGlobalVariable = 2;

// Good 
global g_myVariable = 1;
// Bad
const j = "Luke";

// Good
const jedi = "Luke";
function divider(a, b)
{
  return a / b;
}
// Bad
var boolIsActive = true;

// Good
var isActive = true;

This is because there is no such thing as private variables in Javascript or HISE script. Indicating that these fully public variables are private could mislead a developer into thinking a change won't affect another part of the program.

// Bad
namespace Clown
{
    const _Bozo = "Twister";
    const _balloonAnimals = ["Cat", "Dingo", "Robert Frost"];
}

Types

var



> var may occasionally be needed in other parts of your script but avoid using it if there is an alternative.



> The scope of a var is not limited to the namespace in which it is declared.

// Bad
namespace MyNamespace
{
  var aVariable = 10;
}

Console.print(aVariable); // 10

// Good
namespace MyNamespace
{
  reg aVariable = 10;
}

Console.print(aVariable); // undefined
Console.print(MyNamespace.aVariable); // 10

const



> There is no functional difference between the two so the var is unnecessary.

reg

local

global

They impact portability, complicate maintenance, obscure program flow, and reduce readability.

This is because the HISE editor will highlight the global keyword, improving readability.

// Bad
Globals.g_myGlobalVariable = 50;

// Good
global g_myGlobalVariable = 50;

Numbers

// Bad 
const foo = .1;
  
// Good
const foo = 0.1;

You can use built-in constants, define consts, or use an enum.

// Bad 
g.fillAll(0xffff0000);

// Good
g.fillAll(Colours.red);



Strings

Objects and Arrays

const NUM_ELEMENTS = 100000;
const badList = [];
const goodList = [];

// Bad
Console.start();

for (i = 0; i < NUM_ELEMENTS; i++)
  badList.push(i);

Console.stop(); // ~41ms

//Good
Console.start();

goodList.reserve(NUM_ELEMENTS);

for (i = 0; i < NUM_ELEMENTS; i++)
  goodList.push(i);

Console.stop(); // ~39ms
const myObject = {a: 1, b: 2, c: 3};
const myArray = [
  1,
  2,
  3,
  4
];
const theFellowship = {
   Wizard: "Gandalf",
   Dwarf: "Gimli",
   Elf: "Legolas",
   Hobbit: "Frodo"
 };
const animals = {dog: "woof", cat: "meow", fox: "?"};
const notes = ["a", "a#", "b", "c", "c#", "d", "d#"];
const dogs = {
  Larry: "Mr White",
  Freddy: "Mr Orange",
  Vic: "Mr Blonde",
  Quentin: "Mr Brown",
  Chris: "Eddie"
};
const myObject = {
  property1: "value1",
  property2: 123,
  "foo-bar": "Hello World"
};

UI Components

If components are defined by themselves in a namespace, init, or factory function then such a comment may not be necessary.

// btnEQBypass - EQ bypass toggle button
const btnEQBypass = Content.getComponent("btnEQBypass");
btnEQBypass.set("saveInPreset", false);
btnEQBypass.set("text", "EQ Bypass");
btnEQBypass.setControlCallback(onbtnEQBypassControl);

inline function onbtnEQBypassControl(component, value)
{
  doSomething();
}

It's not necessary to use the abbreviation in the name of the component as the component type is shown in the component list, however it can be helpful to do so.

// Bad
const expression = Content.addKnob("Expression");
const expressionKnob = Content.addKnob("Expression");

// Good
const knbExpression = Content.addKnob("knbExpression");

This makes it possible to manipulate multiple components at once using loops.

const knbADSR = [];
knbADSR[0] = Content.getComponent("knbAttack");
knbADSR[1] = Content.getComponent("knbRelease");

for (x in knbADSR)
  x.setControlCallback(onknbADSRControl);

This is provided by right-clicking a component in the component list and selecting "Create custom callback definition".

// pnlMain - Main content panel
inline function onpnlMainControl(component, value)
{
	//Add your custom logic here...
};

Content.getComponent("pnlMain").setControlCallback(onpnlMainControl);
// pnlMain - Main contents panel
const pnlMain = Content.getComponent("pnlMain");
pnlMain.set("saveInPreset", false);
pnlMain.setControlCallback(onpnlMainControl);

inline function onpnlMainControl(component, value)
{      
}

This follows the default HISE naming convention.

inline function onbtnEQBypassControl(component, value)
{
}
pnlMain.setPaintRoutine(function(g)
{
});
const tabbarPaintRoutine = function(g)
{
  g.fillAll(Colours.red);
}

pnlTab1.setPaintRoutine(tabbarPaintRoutine);
pnlTab2.setPaintRoutine(tabbarPaintRoutine);

This makes the script more shareable with other people and projects as both the UI layout and logic will be contained within a single file.

// Bad
const btnPresetBrowser = Content.getComponent("btnPresetBrowser");

// Good
const btnPresetBrowser = Content.addButton("btnPresetBrowser", 0, 10);

Operators

This improves readability and clarifies the developer’s intention.

// Bad
const bar = a + b / c * d;

// Good
const bar = a + (b / c) * d;
// Bad
const x = 1+y - 2*(z / 3);

// Good
const x = 1 + y - 2 * (z / 3);

Functions

function addNumbers(a, b)
{
  return a + b;
}
// Bad
if (x == 1)
  return "foo";
else
  myFunction();

// Good
if (x == 1)
  return "foo";

myFunction();

Namespaces

SVG Paths

A single variable can be reused to declare all svg data arrays before they are converted to paths.

namespace Paths
{
  reg svgData;
  
  // My logo
  svgData = [110,109,51,179,243];
  const logo = Content.createPath();
  logo.loadFromData(svgData);
  
  // My heading
  svgData = [113,10,0,0,25];
  const myHeading = Content.createPath();
  heading.loadFromData(svgData);
}

pnlLogo.setPaintRoutine(function(g))
{
  g.setColour(Colours.blue);
  g.fillPath(Paths.logo, [x, y, w, h]);
}

Enumeration

namespace Articulations
  {
    const Sustain = 0;
    const Staccato = 1;
    const Spiccato = 2;
    const FastAttack = 3;
  }

Custom HISE Additions