Console Commands

Revision as of 03:30, 22 August 2024 by K1ll3rB33 (talk | contribs)

Console Commands extend the functionality of the sequencer beyond what is possible with the UI. To access the console, press Ctrl + Shift + J for Chromium browsers, or Ctrl + Shift + K for Firefox.

The Chrome developer console
The Chrome developer console

To run a command using the console, just type it in and press enter. The console executes JS code, so if you want to go beyond copying/pasting the commands on this page, try learning some JS.

It's important to keep a few things in mind when using the console:

  • Not all of the commands below have undo/redo support. So if you make a change it might not be possible to undo it.
  • It's possible to corrupt your sequence using the console. So if you're new to this it's highly recommended that you save your sequence before messing with the console.
  • The fundamental time units used by the sequencer are quarter notes, starting at 0. So t=10 would mean the half-beat after the second beat. If you set the grid to 1/4 (the default) the grid lines match this time unit. Markers can only be placed at whole number time steps: t=0, 1, 2, etc.
  • Instruments are identified using a number, also known as the ID. The current instrument is stored in the global variable "instrument". So to figure out the number for an instrument, just choose that instrument in the UI, type "instrument", and press enter. You can also just pass "instrument" directly to use the current instrument, eg "setDetune(instrument, 1200)".
  • The console can be very noisy, but all the log spam comes from the ads. You can restrict the console to only show logs from the sequencer itself by enabling "Selected context only" in the console settings (see image). This works in Chrome, but there will be similar filters in any browser.
  • Since 2023, if the browser questions pasting, you must use the command "allow pasting."
  • Alternatively, you can type your command preceded by "javascript:" in the URL box to run it
Silencing log spam
To silence log spam, click the console settings button, then enable "Selected context only".

Basic techniques

You can change a lot of the sequencer options in the console. This can be useful to go beyond the values usually allowed in the UI.

Grid

setGrid(value)

Changes the size of the grid. The way the value works is a bit confusing: it's the number of grid lines per time unit (per quarter note). So a 1/4 grid has a value of 1, and a 1/8 grid has a value of 2. You can use this formula to work out the value: value = 0.25 / grid. No undo/redo support.

Time signature

setTimeSig(timeSig)

Sets the time signature of the sequence. Time signatures on OS are always N/4 (so 3/4, 4/4, 5/4 etc), and the timeSig parameter is just the N. So to set the time signature to 7/4, use "setTimeSig(7)". If you want something more exotic, you'll need to find the closest equivalent in N/4. So 6/8 could be represented as 3/4, and 7/16 could be represented as 7/4 with a faster tempo. No undo/redo support.

Detune

setDetune(instrument, detune)

Sets the detune of an instrument. Detune values are measured in cents, where each semi-tone is 100 cents. So to detune a full octave up, set the detune to 1200. The usual limit in the UI is -1200 to 1200, but you can use this function to set it to more extreme values. No undo/redo support.

WARNING: Firefox does not support detunes outside -1200 to 1200, so using extreme values will mean your sequence will only play correctly on Chromium browsers.

hZ to Detune

K1ll3rB33 has made a script that detunes it from 440hz to a custom one. Simply replace "frequency" with your desired frequency in hZ.

setDetune(instrument, 1200*Math.log(frequency/440)/Math.log(2))

Reverb and distortion

setReverbVolume(instrument, volume)
setDistortVolume(instrument, volume)

Sets the reverb volume and distort volume of an instrument. The volume goes from 0 to 1 in the UI, but you can set it to whatever you like using these functions. No undo/redo support.

Equalizer

setEqHigh(instrument, value)
setEqMid(instrument, value)
setEqLow(instrument, value)

Sets the EQ of an instrument. The values only go from -48 to 48 in the UI, but you can set them to any value using these functions. No undo/redo support.

Panning

setPan(instrument, value)

Sets the panning of an instrument. The value can go from -1 to 1 in the UI, but this function can set it to anything. No undo/redo support.

Volume

Instrument Volume:

setInitialInstrumentVolume(instrument, value)

Sets the volume of an instrument. The value can go from -1 to 1 in the UI, but this function can set it to anything. No undo/redo support. Does not update the UI automatically, either update the UI manually or use fillAdvancedInstrumentSettings() and fillSimpleInstrumentSettings()

Sequence Volume:

setInitialSongVolume(value)

Sets the volume of the sequence. The value can go from -1 to 1 in the UI, but this function can set it to anything. No undo/redo support. Does not update the UI.

Intermediate techniques

Most of these functions act on the currently selected notes. Select the notes you want to edit, then run the function in the console. I refer to the sequence of selected notes as the selected segment.

In JS, function parameters can have default values. This means if you don't pass a value to that parameter, it will default to some value. A lot of the functions in this section have default parameters. They're written in the documentation like this: "fadeNotes(fadeIn = false)". This means the "fadeIn" parameter defaults to false, so instead of writing "fadeNotes(false)" you can just write "fadeNotes()".

The functions in this section are defined here. If you know some JS, you can read this file to learn how they work and create your own.

Fade notes

fadeNotesIn()
fadeNotesOut()

Fades the selected notes in or out. FadeNotesIn fades in the segment from 0 volume to the original volume, and fadeNotesOut fades the notes from their original volume to 0.

Includes undo/redo support.

Stretch notes

stretchNotes(factor)

Stretches or squishes the selected segment by the given factor. A factor of more than 1 will make the selected segment longer, and less than 1 will make it shorter. A factor less than 0 will reverse the segment.

It also works on selected markers, but since marker times are quantized to whole number time steps, they might not be moved to exactly the right spot. Also, reversing markers is complicated, and not all marker sequences are reversible, so negative factors are not supported if you have markers selected.

Includes undo/redo support.

Truncate notes

truncateNotesAt(time)

Cuts off all the selected notes at the specified time. If a note extends past this time, its length will be truncated. If a note begins after this time it will be deleted.

Includes undo/redo support.

Convert to detune markers

convertToDetuneMarkers(startNote = 'C5')

Moves all the selected notes to the "startNote", then creates detune markers to detune each note back to its original pitch. For example, if you select a "D5" and then run this with the default startNote, it will move the note to "C5" and create a detune marker to detune it up 200 cents back to "D5". This is useful for making clear melodies on instruments that get muddy when there are a lot of notes.

Due to the limitations of detune markers, this will only work if the melody sticks to whole number time steps (ie lines up with the quarter note grid), and there's only ever one note at a particular time. This function ignores drum kit instruments.

Includes undo/redo support.

Convert to Marimba

convertToMarimba()

Clones and detunes the "2023 Drum Kit" for each note in the selection, then moves each note to the closest octave of the three "Marimba" notes in the kit. This is useful for using the marimba as its own instrument, and works with multiple notes at a time. This function ignores drum kit instruments.

Includes undo/redo support.

Humanize

humanize(volumeVariation = 0.2, timeVariation = 0.1)

Randomly alters the volume and start time of all the selected notes by a small amount. This is designed to simulate a human playing the sequence. You can choose your own variation amounts, or just run "humanize()" to use the defaults.

Each note volume is multiplied by a random value between 1 - volumeVariation and 1 + volumeVariation (so 0.8 to 1.2 by default). Each note start time is shifted by a random value between -timeVariation and timeVariation (ie timeVariation is measured in quarter notes).

Note that if you use this function on a note that touches the end of of the last measure of your sequence, there's a 50/50 chance it will be moved to the right (past the end of the sequence) causing your sequence to loop one measure late. You can fix this by manually changing the length of the note to snap it back to the end of that final measure.

Includes undo/redo support.

Remix notes

remixNotes(chunkSize = 4, avgChunksPerUnmixedSection = 2, avgChunksPerMixedSection = 2, avgMixedSectionsPerUnmixedSection = 1)

Divides the selected segment into chunks and mix them around. This is mainly just for fun but can be handy to generate a breakdown by remixing a drum loop or to reshuffle a melody if you need a bit of inspiration. The chunkSize parameter controls how large the chunks are (in quarter notes), and the other parameters control how the chunks are mixed up.

Includes undo/redo support.

Reset all instrument settings

resetAllInstrumentSettings()

Resets the settings of all the instruments in the sequence.

WARNING: No undo/redo support.

removeAllMarkers()

Deletes all the markers in the sequence.

WARNING: No undo/redo support.

Advanced techniques

You will need to know some JS to use these functions. In particular, we're going to be using a lot of lambda functions, so familiarize yourself with these first. This section also assumes you know basic programming concepts like variables, objects, loops, and if statements.

Working with the selection

Most of the functions in the previous section work on the selected notes and markers. The notes and markers selected in the UI are stored in the "selection" object.

console.log(selection.notes)
console.log(selection.markers)
console.log(selection.getTimeSpan())

You can find all the methods on the selection object here. It's sometimes useful to be able to loop over all the notes or markers in the selection, but if you want to modify the notes in the selection, use tweakNotes below.

for (const note of selection.notes) {
  console.log(note);
}

for (const marker of selection.marker) {
  console.log(marker);
}

You can also add or remove markers and notes from the selection using the selectNote, selectMarker, deselectNote, and deselectMarker methods. However, it's usually easier to use these functions:

selectNotesIf(predicate, addToSelection = false)
selectMarkersIf(predicate, addToSelection = false)

These functions take a predicate (a function that returns true or false) and select all the notes or markers for which the predicate returns true. If addToSelection is true, these notes or markers are added to the existing selection, otherwise, they replace the current selection. Both these functions include undo/redo support.

For example, say you wanted to delete the thumbnail notes from a sequence, so you can change the thumbnail. Thumbnail notes all have 0 volume, so you can just select all the notes with 0 volume:

selectNotesIf(n => n.volume == 0)

This selects all the thumbnail notes, so now you can delete them as normal (though the UI focus will still be on the console, so you'll have to click on the sequencer before you press delete).

Tweaking notes

tweakNotes(tweakFunction)

This is probably the single most useful console function. It basically runs tweakFunction on every selected note and has undo/redo support for any changes that tweakFunction makes to the note. It's roughly equivalent to:

for (const note of selection.notes) {
  tweakFunction(note);
}

There are a few problems with doing a for loop like this and manually tweaking the notes. For starters, you won't get undo/redo support, but the bigger problem is that there are a few optimizations that OS does based on the note time, instrument, length, or pitch that require extra work if you modify those parameters (namely "song.moveNote()" and "song.updateLoopTime()"). Failure to do so could corrupt your sequence. tweakNotes takes care of this work for you so you don't have to worry about it.

The fields of the note object that you can modify are:

The fields of the Note object
Name Type Description Can modify?
instrument Integer The instrument id, eg 22 for sitar Only with tweakNotes
type String The pitch or keyboard note, eg 'C5' or 'F#7' Only with tweakNotes
time Number The time when the note starts in standard time units (quarter notes) Only with tweakNotes
length Number The length of the note in standard time units Only with tweakNotes
volume Number The volume of the note, usually from 0 to 1. Always

If you console.log the note you'll see other internal fields, but DO NOT MODIFY THESE.

For example, you can halve the volume like this (eg for a custom delay effect, copy, shift, then tweak the volume):

tweakNotes(n => n.volume *= 0.5)

We can also use tweakNotes to quantize the note times. This code snaps the notes to the nearest grid line (grid is a global variable that contains the grid value, see setGrid above):

tweakNotes(n=>n.time = Math.round(n.time * grid) / grid)

Another example is the humanize function, which is built on tweakNotes (in fact, several of the other functions could be rewritten in terms of tweakNotes):

function humanize(volumeVariation = 0.2, timeVariation = 0.1) {
  tweakNotes(n => {
    n.volume *= 1 + volumeVariation * (2 * Math.random() - 1);
    n.time += timeVariation * (2 * Math.random() - 1);
    if (n.time < 0) n.time = 0;
  });
}

Tweak notes can also be used to generate the lengthening sawtooth effect from Ganymede:

const span = selection.getTimeSpan();
tweakNotes(n => {
  n.length *= 0.5 + (n.time - span.min) / (span.max - span.min);
});

A more complicated example is adding swing to your selected notes:

tweakNotes(n => {
  const t = n.time / 4;
  const intTime = Math.floor(t);
  const fracTime = t - intTime;
  const newFracTime = fracTime <= 0.5 ? fracTime * 4 / 3 : (fracTime - 0.5) * 3 / 4 + 2 / 3;
  n.time = (intTime + newFracTime) * 4;
});

Tweaking markers

There is currently no marker equivalent of tweakNotes. Markers also have optimizations that mean you can't modify the time, instrument, or setting, without doing extra work. The only things you can safely modify are the value and the blend. If you need to modify the other parameters, you're better off asking the devs to add a tweakMarkers function, rather than trying to modify them and corrupting your sequence.

The fields of the Marker object
Name Type Description Can modify?
instrument Integer The instrument id, eg 19 for xylophone Never
time Integer The marker's time (quarter notes, must be a whole number) Never
setting Integer The setting (one of the kMarkerSetting... constants, eg kMarkerSettingBpm) Never
value Dynamic The marker value. Its type depends on the marker setting. Always
blend Boolean Whether or not the marker is blended. Always

With these limitations, the most useful thing you can do is set the marker value to a value outside the normal range (like we did for the instrument settings above). This snippet sets all the selected detune markers to detune up by 2 octaves:

for (const marker of selection.markers) {
  if (marker.setting == kMarkerSettingInstrumentDetune) {
    marker.value = 2400;
  }
}

If you really need to change the instrument, time, or setting, delete the marker and create a new one (see below for details).

You can also use "song.getAllMarkersAtTime(t)" to get a list of all the markers at a specific time, but selecting them in the UI and then iterating over "selection.markers" is easier.

Creating notes

You can create Note objects using "new Note", but you also have to manually add them to the song, like so:

song.addNote(new Note(song, type, time, length, instrument, volume))

For example, let's generate a random melody:

for (let i = 0; i < 16; ++i) {
  song.addNote(new Note(song, 'CDEFGAB'[Math.floor(Math.random() * 7)] + '4', i, 1, instrument, 1));
}

Whenever you make changes to the notes of a sequence (adding, removing, moving, etc), the changes won't be visible until the sequencer view is updated. You can either run "SequencerView.repaint()", or just move the view a bit (eg scroll). tweakNotes handles this for you, but in this example, you need to do it manually.

Adding notes like this doesn't have undo/redo support.

Deleting notes

If you have a Note object, you can remove it from the sequence like this:

song.removeNote(note)

This doesn't actually delete the Note object, just removes it from the sequence. In fact you can add it again if you want, using "song.addNote(note)".

Let's take another look at our earlier example, where we wanted to delete the thumbnail notes. Instead of using selectNotesIf and then manually deleting them, we could automatically delete them.

for (const note of song.notes) {
  if (note.volume == 0) {
    song.removeNote(note);
  }
}

Removing notes like this doesn't have undo/redo support.

Creating markers

addMarker(time, setting, instrument, value, blend)

This function adds a marker to the song. The parameters are the same as in the marker field table above. Returns the newly created Marker object. Includes undo/redo support.

Deleting markers

removeMarker(marker)

Removes the marker from the sequence. Includes undo/redo support.