Home Manual Reference Source Test

Function

Static Public Summary
public

async buildParameters(steps: Step[]): Promise<{steps: Step[], schemas: Object<string, Object[]>, parameters: Object}>

Builds the initial composition parameters (theta) for a list of steps by calling schema() on each.

public

chordGraph(mode: 'major' | 'minor', preset: Object<string, {edges: Object<string, number>, chromatic: string[]}>): { nodes: string[], activeNodes: string[], edges: [string,string][], costs: Map<string,number>, tonic: string, }

Creates the chord digraph G = (V, E, w) for a given mode, optionally tuned by a corpus preset (pi).

public

chordName(node: string, keyIndex: number): string

Roman-numeral display: with keyIndex returns the concrete chord name; without keyIndex returns the function-label form for UI dropdowns.

public

async compose(parameters: Object, steps: Step[], hooks: Object): Promise<{ parts: Object[], labelCache: Object, midi: ?Object, failedParts: string[], solvedParts: number, } | null>

Run the 3-step composition pipeline (form, chords, notes); pure orchestration via hooks.

public

downloadBlob(data: Blob | string, filename: string): string

Triggers a browser file download from a Blob or a Base64-encoded string.

public

exportParameters(parameters: Object)

Exports the composition parameters (theta) as a JSON file download.

public

generateMidi(parts: Object[], voiceConfig: {voices: number, voice: {instrument: number}[]}, tempo: number): {combinedB64: string, voicesB64: string[]}

Builds MIDI from part notes solutions N (N=1 for per-part preview, N=|parts| for full piece).

public

importParameters(file: File | Blob): Promise<Object>

Reads a parameter JSON file and returns the parsed content.

public

Initialize the MiniZinc WASM engine; must run before any solveModel() call.

public

modelChords(parameters: Object, graph: Object): string

Builds the MiniZinc COP for Step 2: shortest path of fixed length over the chord graph.

public

modelNotes(parameters: Object, time: {subdivision: number, subBeatsPerBar: number, numSubBeats: number, activeBeats: Set<number>[]}): string

Builds the MiniZinc CSP for Step 3: assigns one note per active sub-beat and voice.

public

noteNames(keyIndex: number): string[]

Chromatic note-name array (12 entries starting from C); flat-preferring by default (also used as MIDI key-signature labels), sharp-preferring when keyIndex (kappa) is a sharp-favouring key.

public

async solveModel(modelName: string, modelString: string, solverId: string): Promise<{solution: ?Object, statistics: ?Object, status: string}>

Wrap MiniZinc's event-based solver as a Promise. Implements the solve() call from Thesis' Algorithm.

public

submitFeedback(dialog: HTMLDialogElement, midiB64: string, parameters: Object, report: Object)

Collects feedback form values, downloads the MIDI file, and opens a pre-filled mailto: link with the form data and composition parameters (theta) in the body.

public

timeGrid(voice: {duration: string}[], voices: number, numerator: number, bars: number): {subdivision: number, subBeatsPerBar: number, numSubBeats: number, activeBeats: Set<number>[]}

Per-part time-grid; subdivision (tau) is the LCM of voice durations.

Static Public

public async buildParameters(steps: Step[]): Promise<{steps: Step[], schemas: Object<string, Object[]>, parameters: Object}> source

import {buildParameters} from 'constraint-composer/src/controller/parameters.js'

Builds the initial composition parameters (theta) for a list of steps by calling schema() on each.

Params:

NameTypeAttributeDescription
steps Step[]

Step instances.

Return:

Promise<{steps: Step[], schemas: Object<string, Object[]>, parameters: Object}>

Step instances unchanged plus a schemas-by-step.id map and the parameters object.

Test:

public chordGraph(mode: 'major' | 'minor', preset: Object<string, {edges: Object<string, number>, chromatic: string[]}>): { nodes: string[], activeNodes: string[], edges: [string,string][], costs: Map<string,number>, tonic: string, } source

import {chordGraph} from 'constraint-composer/src/model/theory.js'

Creates the chord digraph G = (V, E, w) for a given mode, optionally tuned by a corpus preset (pi).

Params:

NameTypeAttributeDescription
mode 'major' | 'minor'
  • optional
  • default: 'major'

Tonal mode (mu); drives node set and default costs.

preset Object<string, {edges: Object<string, number>, chromatic: string[]}>
  • optional
  • nullable: true

Corpus preset (pi) keyed by mode (mu); per mode edges (corpus-frequency nu thresholded by epsilon) and chromatic nodes; null falls back to DEFAULTS.

Return:

{ nodes: string[], activeNodes: string[], edges: [string,string][], costs: Map<string,number>, tonic: string, }

nodes (V, incl. chromatic), activeNodes (subset participating in edges), edges (E, valid-edge keys of costs), costs (w, edge cost map), tonic ("I" or "i").

Test:

public chordName(node: string, keyIndex: number): string source

import {chordName} from 'constraint-composer/src/model/theory.js'

Roman-numeral display: with keyIndex returns the concrete chord name; without keyIndex returns the function-label form for UI dropdowns.

Params:

NameTypeAttributeDescription
node string

Roman-numeral chord symbol; "NC" and unknowns pass through.

keyIndex number
  • optional

0-11 (kappa); omitted means label form.

Return:

string

Example:

chordName("I");       // "I: Tonic"  (label form, key-independent)
chordName("V", 0);    // "G"         (dominant in C major)
chordName("vi", 0);   // "Am"        (minor from lowercase numeral)
chordName("viio", 0); // "Bo"        (diminished from "o" suffix)
chordName("NC", 0);   // "NC"

Test:

public async compose(parameters: Object, steps: Step[], hooks: Object): Promise<{ parts: Object[], labelCache: Object, midi: ?Object, failedParts: string[], solvedParts: number, } | null> source

Run the 3-step composition pipeline (form, chords, notes); pure orchestration via hooks. Step 2 + Step 3 results are cached by part label so repeated labels (e.g. A in A-B-A-C-A) solve each unique label only once. View layer assembles the display table from labelCache. Implements roughly the Thesis' Algorithm.

Params:

NameTypeAttributeDescription
parameters Object

Top-level parameters (theta) keyed step1/step2/step3. Sub-shapes per Step1Parts#schema, Step2Chords#schema, Step3Notes#schema.

steps Step[]

Pipeline order [Step1Parts, Step2Chords, Step3Notes].

hooks Object

Side-effect callbacks for solver runs and UI updates.

hooks.log function(msg: string): void

Status-line + console-log callback.

hooks.loadPreset function(style: string): Promise<Object|null>

Resolves a corpus preset id (pi) to a preset object; null on miss.

hooks.solveStep function(step: Step, partLabel: string, mznSource: string): Promise<Object|null>

Runs a model on the configured solver; returns the solution (null when no solution).

hooks.updateGraph function(chordGraph: Object): void

Renders the chord-graph data (chordGraph) into the view layer.

hooks.onProgressionLine function(line: {label: string, chords: string[]}): void

Emits one solved Roman-numeral progression line per Step 2 solve.

Return:

Promise<{ parts: Object[], labelCache: Object, midi: ?Object, failedParts: string[], solvedParts: number, } | null>

Orchestration result; null when Step 1 produces no parts.

Emit:

progressionLine

Test:

public downloadBlob(data: Blob | string, filename: string): string source

import {downloadBlob} from 'constraint-composer/src/view/download.js'

Triggers a browser file download from a Blob or a Base64-encoded string.

Params:

NameTypeAttributeDescription
data Blob | string

Blob or Base64 encoded binary (treated as audio/midi).

filename string
  • optional

Output name; defaults to UTC-timestamp .mid.

Return:

string

Filename stem (without extension).

Example:

downloadBlob(blob);                  // "2026-05-09T14-23-07" (default UTC timestamp)
downloadBlob(b64String, "song.mid"); // "song" (custom filename, returns stem)

Test:

public exportParameters(parameters: Object) source

import {exportParameters} from 'constraint-composer/src/controller/parameters.js'

Exports the composition parameters (theta) as a JSON file download.

Params:

NameTypeAttributeDescription
parameters Object

Top-level parameters (theta) to serialize as JSON.

Test:

public generateMidi(parts: Object[], voiceConfig: {voices: number, voice: {instrument: number}[]}, tempo: number): {combinedB64: string, voicesB64: string[]} source

Builds MIDI from part notes solutions N (N=1 for per-part preview, N=|parts| for full piece). Per-part time-signature and key/mode changes are emitted as mid-track meta events, so a multi-section piece (e.g. C major 4/4 -> A minor 3/4) renders with correct meta events at every part boundary.

Params:

NameTypeAttributeDescription
parts Object[]

Per-part records: solution, subdivision, numSubBeats, numerator, denominator, key, mode. Empty input returns null.

voiceConfig {voices: number, voice: {instrument: number}[]}

Global voice config.

tempo number

BPM for the entire combined track.

Return:

{combinedB64: string, voicesB64: string[]} (nullable: true)

Note: midi-writer-js hardcodes 128 PPQ, so triplet sub-beats (subdivision=3) drift by ~1 tick per quarter.

Test:

public importParameters(file: File | Blob): Promise<Object> source

import {importParameters} from 'constraint-composer/src/controller/parameters.js'

Reads a parameter JSON file and returns the parsed content. Restores composition parameters (theta) from a previously exported JSON file.

Params:

NameTypeAttributeDescription
file File | Blob

Source containing serialized parameters (theta) as JSON text.

Return:

Promise<Object>

Parsed parameter object (theta).

Throw:

Error

If the file cannot be read or the content is not valid JSON.

Test:

public async initSolvers(): Promise<Array<Object>> source

Initialize the MiniZinc WASM engine; must run before any solveModel() call.

Return:

Promise<Array<Object>>

List of available solvers (each with id etc.).

Test:

public modelChords(parameters: Object, graph: Object): string source

Builds the MiniZinc COP for Step 2: shortest path of fixed length over the chord graph. MZN nodes are 1-indexed: solver path int i refers to graph.nodes[i-1].

Params:

NameTypeAttributeDescription
parameters Object

Step-2 form params; see Step2Chords#schema.

graph Object

Chord graph from chordGraph.

Return:

string

MiniZinc source.

Test:

public modelNotes(parameters: Object, time: {subdivision: number, subBeatsPerBar: number, numSubBeats: number, activeBeats: Set<number>[]}): string source

Builds the MiniZinc CSP for Step 3: assigns one note per active sub-beat and voice. Encodes T1-T3, R4, R9, H12, H19, M1, M9, M14 from the Thesis.

Params:

NameTypeAttributeDescription
parameters Object

Step-3 form params plus chord progression and tempo; see Step3Notes#schema.

time {subdivision: number, subBeatsPerBar: number, numSubBeats: number, activeBeats: Set<number>[]}

R4+R9: Time-grid built by Step 3: subdivision (tau), T, per-voice onset sets (omicron).

Return:

string

MiniZinc source.

Test:

public noteNames(keyIndex: number): string[] source

import {noteNames} from 'constraint-composer/src/model/theory.js'

Chromatic note-name array (12 entries starting from C); flat-preferring by default (also used as MIDI key-signature labels), sharp-preferring when keyIndex (kappa) is a sharp-favouring key.

Params:

NameTypeAttributeDescription
keyIndex number
  • optional

0-11 (kappa); omitted means flat-preferring.

Return:

string[]

Example:

noteNames();    // ["C", "Db", "D", "Eb", ...] flat default
noteNames(0);   // ["C", "C#", "D", "D#", ...] C major prefers sharps
noteNames(5);   // ["C", "Db", "D", "Eb", ...] F major prefers flats

public async solveModel(modelName: string, modelString: string, solverId: string): Promise<{solution: ?Object, statistics: ?Object, status: string}> source

Wrap MiniZinc's event-based solver as a Promise. Implements the solve() call from Thesis' Algorithm.

Params:

NameTypeAttributeDescription
modelName string

Virtual filename for the MZN engine.

modelString string

MZN source: variables + hard constraints (H) + optional objective (S).

solverId string

Backend id (e.g. "gecode").

Return:

Promise<{solution: ?Object, statistics: ?Object, status: string}>

Throw:

Error

If the solver fails to initialize, or wraps any solver-side failure via cause-chain.

Test:

public submitFeedback(dialog: HTMLDialogElement, midiB64: string, parameters: Object, report: Object) source

import {submitFeedback} from 'constraint-composer/src/view/feedback.js'

Collects feedback form values, downloads the MIDI file, and opens a pre-filled mailto: link with the form data and composition parameters (theta) in the body.

Params:

NameTypeAttributeDescription
dialog HTMLDialogElement

Wrapper element for the user-feedback form.

midiB64 string

Base64-encoded MIDI data.

parameters Object

Top-level parameters (theta) attached as JSON to the mail body.

report Object

Mail data beyond the raw parameters.

report.aspects Object<string, Object<string, string>>

Nested tristate answers grouped by [data-tri-group] fieldset.

report.chordProgression Array<{label: string, chords: string[]}>
  • optional

Per-form-part Roman-numeral progression as heard; appended as raw text when present.

Test:

public timeGrid(voice: {duration: string}[], voices: number, numerator: number, bars: number): {subdivision: number, subBeatsPerBar: number, numSubBeats: number, activeBeats: Set<number>[]} source

Per-part time-grid; subdivision (tau) is the LCM of voice durations.

Params:

NameTypeAttributeDescription
voice {duration: string}[]

Per-voice configs; only the first 'voices' entries are active.

voices number

Active voice count (|S|).

numerator number

Time-signature numerator (zeta).

bars number

Bar count for this part (iota).

Return:

{subdivision: number, subBeatsPerBar: number, numSubBeats: number, activeBeats: Set<number>[]}

Subdivision (tau); sub-beats per bar; T = iota * zeta * tau; per-voice onset sets (omicron).

Test: