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 |
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 |
async initSolvers(): Promise<Array<Object>> 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 |
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:
| Name | Type | Attribute | Description |
| steps | Step[] | Step instances. |
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:
| Name | Type | Attribute | Description |
| mode | 'major' | 'minor' |
|
Tonal mode (mu); drives node set and default costs. |
| preset | Object<string, {edges: Object<string, number>, chromatic: string[]}> |
|
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"). |
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.
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"
public async compose(parameters: Object, steps: Step[], hooks: Object): Promise<{ parts: Object[], labelCache: Object, midi: ?Object, failedParts: string[], solvedParts: number, } | null> source
import {compose} from 'constraint-composer/src/controller/composer.js'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:
| Name | Type | Attribute | Description |
| 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 |
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.
Example:
downloadBlob(blob); // "2026-05-09T14-23-07" (default UTC timestamp)
downloadBlob(b64String, "song.mid"); // "song" (custom filename, returns stem)
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:
| Name | Type | Attribute | Description |
| parameters | Object | Top-level parameters (theta) to serialize as JSON. |
public generateMidi(parts: Object[], voiceConfig: {voices: number, voice: {instrument: number}[]}, tempo: number): {combinedB64: string, voicesB64: string[]} source
import {generateMidi} from 'constraint-composer/src/controller/tracks.js'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.
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:
- generateMidi
- integration: midi-writer-js emits a real MIDI binary with the expected header from a realistic solution
- integration: midi-writer-js produces one MTrk per voice in the combined file when multiple voices are present
- generateMidi emits per-voice tracks with header meta and one NoteEvent per sub-beat for a 2-voice 2-sub-beat input
- generateMidi extends preceding note via ties as a single T384 event and emits Cm key signature in minor mode; accumulates rests and orphan-ties as wait T256
- generateMidi returns null on empty parts input such as when all step-3 parts are UNSAT
- generateMidi emits TimeSig and KeySig at every multi-part boundary; scans ties across boundaries so held notes extend and meta events flush after
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:
| Name | Type | Attribute | Description |
| file | File | Blob | Source containing serialized parameters (theta) as JSON text. |
Throw:
If the file cannot be read or the content is not valid JSON. |
public async initSolvers(): Promise<Array<Object>> source
import {initSolvers} from 'constraint-composer/src/controller/solving.js'Initialize the MiniZinc WASM engine; must run before any solveModel() call.
public modelChords(parameters: Object, graph: Object): string source
import {modelChords} from 'constraint-composer/src/model/constraints2.js'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:
| Name | Type | Attribute | Description |
| parameters | Object | Step-2 form params; see Step2Chords#schema. |
|
| graph | Object | Chord graph from chordGraph. |
public modelNotes(parameters: Object, time: {subdivision: number, subBeatsPerBar: number, numSubBeats: number, activeBeats: Set<number>[]}): string source
import {modelNotes} from 'constraint-composer/src/model/constraints3.js'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:
| Name | Type | Attribute | Description |
| 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). |
Test:
- modelNotes
- modelNotes emits a well-formed CSP with include, var declarations and solve satisfy and no minimize
- modelNotes supports scale, chord, atonal and minor pitch domains
- modelNotes emits bare constraints without sentinels when rest_pct = 0; emits holds and rests budget with sentinel-guarded H12, H19, M1 and M9 when rest_pct > 0
- modelNotes emits cross-voice inequality and parallel-fifth and parallel-octave constraints when H12 and M14 toggles are active
- modelNotes emits H19 interval constraints across phi thresholds at <= 0.2, mid and >= 0.8; skips them at neutral 0.5
- modelNotes emits xi seeding with max_interval, anti-osc and empty-cs branches; covers filterByDissonance variants
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:
| Name | Type | Attribute | Description |
| keyIndex | number |
|
0-11 (kappa); omitted means flat-preferring. |
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
import {solveModel} from 'constraint-composer/src/controller/solving.js'Wrap MiniZinc's event-based solver as a Promise. Implements the solve() call from Thesis' Algorithm.
Throw:
If the solver fails to initialize, or wraps any solver-side failure via cause-chain. |
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:
| Name | Type | Attribute | Description |
| 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[]}> |
|
Per-form-part Roman-numeral progression as heard; appended as raw text when present. |
public timeGrid(voice: {duration: string}[], voices: number, numerator: number, bars: number): {subdivision: number, subBeatsPerBar: number, numSubBeats: number, activeBeats: Set<number>[]} source
import {timeGrid} from 'constraint-composer/src/model/theory.js'Per-part time-grid; subdivision (tau) is the LCM of voice durations.
