|
<input type="file" accept=".scsyndef" oninput="window.run(this.files[0])"> |
|
|
|
<script type="module"> |
|
import "https://cdn.jsdelivr.net/gh/gh-canon/stack-snippet-console@master/console.js"; |
|
import { Parser } from "https://esm.sh/binary-parser@2.2.1"; |
|
|
|
window.onerror = e => alert(e.message) |
|
|
|
|
|
window.run = async function(file) { |
|
console.log("Hello?", Parser) |
|
|
|
const pstring = new Parser() |
|
.namely("pstring") |
|
.nest(null, { |
|
type: new Parser() |
|
// This isn't edian depedent |
|
.uint8("length", { |
|
assert: val => val <= 255, // Must be 255 charechters or less |
|
}) |
|
.string("text", { |
|
length: "length", |
|
}), |
|
formatter: a => a.text |
|
}) |
|
const versionDep = new Parser() |
|
.namely("versionDep") |
|
.nest(null, { |
|
type: new Parser() |
|
.choice("val", { |
|
tag: "$root.version", |
|
choices: { |
|
1: new Parser().int16be("value"), |
|
2: new Parser().int32be("value"), |
|
}, |
|
}), |
|
formatter: a => a.val.value, |
|
}); |
|
|
|
|
|
let runs = 0; |
|
|
|
// Build a Supercollider synth def Parser |
|
// Based on spec: https://depts.washington.edu/dxscdoc/Help/Reference/Synth-Definition-File-Format.html |
|
const SynthDefParser = new Parser() |
|
.namely("top") |
|
.useContextVars() |
|
.endianness("big") |
|
.string("fileMagicBytes", { |
|
length: 4, |
|
assert: "SCgf", |
|
}) |
|
.int32("version", { |
|
assert: x => x == 1 || x == 2, |
|
}) |
|
.int16("defsLength") // Number of synths in this file |
|
.array("synthDefs", { |
|
length: "defsLength", |
|
type: new Parser() |
|
.endianness("big") // Set this to parse as big-edian numbers |
|
.nest("name", { type: "pstring", }) // Name of the synthdef |
|
|
|
// Constants |
|
.nest("constantsLength", { type: "versionDep", }) // Number of constants |
|
// The values of the constants |
|
.array("constants", { |
|
type: new Parser().floatbe("value"), |
|
length: "constantsLength", |
|
// Extract contents of unnecessary extra object created by the array parser |
|
formatter: arr => arr.map(a => a.value), |
|
}) |
|
|
|
// Synth params |
|
.nest("numParamInitalValues", { type: "versionDep", }) |
|
.array("paramsInitalValues", { |
|
type: new Parser().floatbe("value"), |
|
length: "numParamInitalValues", |
|
formatter: arr => arr.map(a => a.value), |
|
}) |
|
// The synth parameter names |
|
.nest("paramNamesLength", { type: "versionDep", }) |
|
.array("paramNames", { |
|
length: "paramNamesLength", |
|
type: new Parser() |
|
.nest("name", { type: "pstring" }) |
|
.nest("index", { type: "versionDep", }), // Index in param array |
|
// Puts the synth params in the correct order |
|
formatter: arr => { |
|
let a = new Array(arr.length); |
|
arr.forEach(val => a[val.index] = val.name); |
|
return a; |
|
}, |
|
}) |
|
|
|
// UGens |
|
.nest("numUGens", { type: "versionDep" }) |
|
.array("uGens", { |
|
length: "numUGens", |
|
type: new Parser() |
|
.endianness("big") |
|
.nest("name", { type: "pstring" }) |
|
.int8("calculationRate") // No clue what this does |
|
.nest("inputsLength", { type: "versionDep", }) |
|
.nest("outputsLength", { type: "versionDep", }) |
|
.int16("specialIndex") // No clue what this does either |
|
.array("inputs", { |
|
length: "inputsLength", |
|
type: new Parser() |
|
// UGen index (or constant if x < 0) |
|
.nest("uGenIndex", { type: "versionDep", }) |
|
// uGenIndex < 0 |
|
// ? indexOfConstant |
|
// : uGenOutputIndex |
|
.nest("index", { type: "versionDep", }), |
|
}) |
|
.array("outputs", { |
|
length: "outputsLength", |
|
type: new Parser() |
|
.int8("calculationRate"), |
|
formatter: arr => arr.map(a => a.calculationRate) |
|
}) |
|
}) |
|
.int16("numVariants") |
|
.array("variants", { |
|
length: "numVariants", |
|
type: new Parser() |
|
.nest("name", { type: "pstring" }) |
|
.array("initalValues", { |
|
length: "name", |
|
formatter: a => a.value, |
|
type: new Parser() |
|
.floatbe("value") |
|
}) |
|
}) |
|
}); |
|
|
|
// If this runs then there aren't any errors in the parser syntax |
|
console.log("Yay! No errors in parser syntax!"); |
|
|
|
|
|
let res = SynthDefParser.parse(new Uint8Array(await file.arrayBuffer())); |
|
//res.synthDefs.forEach(synthDef => synthDef.uGens = "truncated") |
|
console.log(res); |
|
console.log("Done!") |
|
|
|
} |
|
</script> |