UNPKG

@strudel/web

Version:

Easy to setup, opiniated bundle of Strudel for the browser.

1,658 lines (1,657 loc) 607 kB
/** * @license Fraction.js v4.3.7 31/08/2023 * https://www.xarg.org/2014/03/rational-numbers-in-javascript/ * * Copyright (c) 2023, Robert Eisele (robert@raw.org) * Dual licensed under the MIT or GPL Version 2 licenses. **/ var Ng = 2e3, te = { s: 1, n: 0, d: 1 }; function Qt(e, t) { if (isNaN(e = parseInt(e, 10))) throw ur(); return e * t; } function Re(e, t) { if (t === 0) throw Ri(); var n = Object.create(Be.prototype); n.s = e < 0 ? -1 : 1, e = e < 0 ? -e : e; var u = Xn(e, t); return n.n = e / u, n.d = t / u, n; } function ra(e) { for (var t = {}, n = e, u = 2, r = 4; r <= n; ) { for (; n % u === 0; ) n /= u, t[u] = (t[u] || 0) + 1; r += 1 + 2 * u++; } return n !== e ? n > 1 && (t[n] = (t[n] || 0) + 1) : t[e] = (t[e] || 0) + 1, t; } var ft = function(e, t) { var n = 0, u = 1, r = 1, i = 0, s = 0, a = 0, l = 1, h = 1, f = 0, m = 1, p = 1, D = 1, y = 1e7, E; if (e != null) if (t !== void 0) { if (n = e, u = t, r = n * u, n % 1 !== 0 || u % 1 !== 0) throw Lg(); } else switch (typeof e) { case "object": { if ("d" in e && "n" in e) n = e.n, u = e.d, "s" in e && (n *= e.s); else if (0 in e) n = e[0], 1 in e && (u = e[1]); else throw ur(); r = n * u; break; } case "number": { if (e < 0 && (r = e, e = -e), e % 1 === 0) n = e; else if (e > 0) { for (e >= 1 && (h = Math.pow(10, Math.floor(1 + Math.log(e) / Math.LN10)), e /= h); m <= y && D <= y; ) if (E = (f + p) / (m + D), e === E) { m + D <= y ? (n = f + p, u = m + D) : D > m ? (n = p, u = D) : (n = f, u = m); break; } else e > E ? (f += p, m += D) : (p += f, D += m), m > y ? (n = p, u = D) : (n = f, u = m); n *= h; } else (isNaN(e) || isNaN(t)) && (u = n = NaN); break; } case "string": { if (m = e.match(/\d+|./g), m === null) throw ur(); if (m[f] === "-" ? (r = -1, f++) : m[f] === "+" && f++, m.length === f + 1 ? s = Qt(m[f++], r) : m[f + 1] === "." || m[f] === "." ? (m[f] !== "." && (i = Qt(m[f++], r)), f++, (f + 1 === m.length || m[f + 1] === "(" && m[f + 3] === ")" || m[f + 1] === "'" && m[f + 3] === "'") && (s = Qt(m[f], r), l = Math.pow(10, m[f].length), f++), (m[f] === "(" && m[f + 2] === ")" || m[f] === "'" && m[f + 2] === "'") && (a = Qt(m[f + 1], r), h = Math.pow(10, m[f + 1].length) - 1, f += 3)) : m[f + 1] === "/" || m[f + 1] === ":" ? (s = Qt(m[f], r), l = Qt(m[f + 2], 1), f += 3) : m[f + 3] === "/" && m[f + 1] === " " && (i = Qt(m[f], r), s = Qt(m[f + 2], r), l = Qt(m[f + 4], 1), f += 5), m.length <= f) { u = l * h, r = /* void */ n = a + u * i + h * s; break; } } default: throw ur(); } if (u === 0) throw Ri(); te.s = r < 0 ? -1 : 1, te.n = Math.abs(n), te.d = Math.abs(u); }; function Tg(e, t, n) { for (var u = 1; t > 0; e = e * e % n, t >>= 1) t & 1 && (u = u * e % n); return u; } function Vg(e, t) { for (; t % 2 === 0; t /= 2) ; for (; t % 5 === 0; t /= 5) ; if (t === 1) return 0; for (var n = 10 % t, u = 1; n !== 1; u++) if (n = n * 10 % t, u > Ng) return 0; return u; } function Rg(e, t, n) { for (var u = 1, r = Tg(10, n, t), i = 0; i < 300; i++) { if (u === r) return i; u = u * 10 % t, r = r * 10 % t; } return 0; } function Xn(e, t) { if (!e) return t; if (!t) return e; for (; ; ) { if (e %= t, !e) return t; if (t %= e, !t) return e; } } function Be(e, t) { if (ft(e, t), this instanceof Be) e = Xn(te.d, te.n), this.s = te.s, this.n = te.n / e, this.d = te.d / e; else return Re(te.s * te.n, te.d); } var Ri = function() { return new Error("Division by Zero"); }, ur = function() { return new Error("Invalid argument"); }, Lg = function() { return new Error("Parameters must be integer"); }; Be.prototype = { s: 1, n: 0, d: 1, /** * Calculates the absolute value * * Ex: new Fraction(-4).abs() => 4 **/ abs: function() { return Re(this.n, this.d); }, /** * Inverts the sign of the current fraction * * Ex: new Fraction(-4).neg() => 4 **/ neg: function() { return Re(-this.s * this.n, this.d); }, /** * Adds two rational numbers * * Ex: new Fraction({n: 2, d: 3}).add("14.9") => 467 / 30 **/ add: function(e, t) { return ft(e, t), Re( this.s * this.n * te.d + te.s * this.d * te.n, this.d * te.d ); }, /** * Subtracts two rational numbers * * Ex: new Fraction({n: 2, d: 3}).add("14.9") => -427 / 30 **/ sub: function(e, t) { return ft(e, t), Re( this.s * this.n * te.d - te.s * this.d * te.n, this.d * te.d ); }, /** * Multiplies two rational numbers * * Ex: new Fraction("-17.(345)").mul(3) => 5776 / 111 **/ mul: function(e, t) { return ft(e, t), Re( this.s * te.s * this.n * te.n, this.d * te.d ); }, /** * Divides two rational numbers * * Ex: new Fraction("-17.(345)").inverse().div(3) **/ div: function(e, t) { return ft(e, t), Re( this.s * te.s * this.n * te.d, this.d * te.n ); }, /** * Clones the actual object * * Ex: new Fraction("-17.(345)").clone() **/ clone: function() { return Re(this.s * this.n, this.d); }, /** * Calculates the modulo of two rational numbers - a more precise fmod * * Ex: new Fraction('4.(3)').mod([7, 8]) => (13/3) % (7/8) = (5/6) **/ mod: function(e, t) { if (isNaN(this.n) || isNaN(this.d)) return new Be(NaN); if (e === void 0) return Re(this.s * this.n % this.d, 1); if (ft(e, t), te.n === 0 && this.d === 0) throw Ri(); return Re( this.s * (te.d * this.n) % (te.n * this.d), te.d * this.d ); }, /** * Calculates the fractional gcd of two rational numbers * * Ex: new Fraction(5,8).gcd(3,7) => 1/56 */ gcd: function(e, t) { return ft(e, t), Re(Xn(te.n, this.n) * Xn(te.d, this.d), te.d * this.d); }, /** * Calculates the fractional lcm of two rational numbers * * Ex: new Fraction(5,8).lcm(3,7) => 15 */ lcm: function(e, t) { return ft(e, t), te.n === 0 && this.n === 0 ? Re(0, 1) : Re(te.n * this.n, Xn(te.n, this.n) * Xn(te.d, this.d)); }, /** * Calculates the ceil of a rational number * * Ex: new Fraction('4.(3)').ceil() => (5 / 1) **/ ceil: function(e) { return e = Math.pow(10, e || 0), isNaN(this.n) || isNaN(this.d) ? new Be(NaN) : Re(Math.ceil(e * this.s * this.n / this.d), e); }, /** * Calculates the floor of a rational number * * Ex: new Fraction('4.(3)').floor() => (4 / 1) **/ floor: function(e) { return e = Math.pow(10, e || 0), isNaN(this.n) || isNaN(this.d) ? new Be(NaN) : Re(Math.floor(e * this.s * this.n / this.d), e); }, /** * Rounds a rational number * * Ex: new Fraction('4.(3)').round() => (4 / 1) **/ round: function(e) { return e = Math.pow(10, e || 0), isNaN(this.n) || isNaN(this.d) ? new Be(NaN) : Re(Math.round(e * this.s * this.n / this.d), e); }, /** * Rounds a rational number to a multiple of another rational number * * Ex: new Fraction('0.9').roundTo("1/8") => 7 / 8 **/ roundTo: function(e, t) { return ft(e, t), Re(this.s * Math.round(this.n * te.d / (this.d * te.n)) * te.n, te.d); }, /** * Gets the inverse of the fraction, means numerator and denominator are exchanged * * Ex: new Fraction([-3, 4]).inverse() => -4 / 3 **/ inverse: function() { return Re(this.s * this.d, this.n); }, /** * Calculates the fraction to some rational exponent, if possible * * Ex: new Fraction(-1,2).pow(-3) => -8 */ pow: function(e, t) { if (ft(e, t), te.d === 1) return te.s < 0 ? Re(Math.pow(this.s * this.d, te.n), Math.pow(this.n, te.n)) : Re(Math.pow(this.s * this.n, te.n), Math.pow(this.d, te.n)); if (this.s < 0) return null; var n = ra(this.n), u = ra(this.d), r = 1, i = 1; for (var s in n) if (s !== "1") { if (s === "0") { r = 0; break; } if (n[s] *= te.n, n[s] % te.d === 0) n[s] /= te.d; else return null; r *= Math.pow(s, n[s]); } for (var s in u) if (s !== "1") { if (u[s] *= te.n, u[s] % te.d === 0) u[s] /= te.d; else return null; i *= Math.pow(s, u[s]); } return te.s < 0 ? Re(i, r) : Re(r, i); }, /** * Check if two rational numbers are the same * * Ex: new Fraction(19.6).equals([98, 5]); **/ equals: function(e, t) { return ft(e, t), this.s * this.n * te.d === te.s * te.n * this.d; }, /** * Check if two rational numbers are the same * * Ex: new Fraction(19.6).equals([98, 5]); **/ compare: function(e, t) { ft(e, t); var n = this.s * this.n * te.d - te.s * te.n * this.d; return (0 < n) - (n < 0); }, simplify: function(e) { if (isNaN(this.n) || isNaN(this.d)) return this; e = e || 1e-3; for (var t = this.abs(), n = t.toContinued(), u = 1; u < n.length; u++) { for (var r = Re(n[u - 1], 1), i = u - 2; i >= 0; i--) r = r.inverse().add(n[i]); if (Math.abs(r.sub(t).valueOf()) < e) return r.mul(this.s); } return this; }, /** * Check if two rational numbers are divisible * * Ex: new Fraction(19.6).divisible(1.5); */ divisible: function(e, t) { return ft(e, t), !(!(te.n * this.d) || this.n * te.d % (te.n * this.d)); }, /** * Returns a decimal representation of the fraction * * Ex: new Fraction("100.'91823'").valueOf() => 100.91823918239183 **/ valueOf: function() { return this.s * this.n / this.d; }, /** * Returns a string-fraction representation of a Fraction object * * Ex: new Fraction("1.'3'").toFraction(true) => "4 1/3" **/ toFraction: function(e) { var t, n = "", u = this.n, r = this.d; return this.s < 0 && (n += "-"), r === 1 ? n += u : (e && (t = Math.floor(u / r)) > 0 && (n += t, n += " ", u %= r), n += u, n += "/", n += r), n; }, /** * Returns a latex representation of a Fraction object * * Ex: new Fraction("1.'3'").toLatex() => "\frac{4}{3}" **/ toLatex: function(e) { var t, n = "", u = this.n, r = this.d; return this.s < 0 && (n += "-"), r === 1 ? n += u : (e && (t = Math.floor(u / r)) > 0 && (n += t, u %= r), n += "\\frac{", n += u, n += "}{", n += r, n += "}"), n; }, /** * Returns an array of continued fraction elements * * Ex: new Fraction("7/8").toContinued() => [0,1,7] */ toContinued: function() { var e, t = this.n, n = this.d, u = []; if (isNaN(t) || isNaN(n)) return u; do u.push(Math.floor(t / n)), e = t % n, t = n, n = e; while (t !== 1); return u; }, /** * Creates a string representation of a fraction with all digits * * Ex: new Fraction("100.'91823'").toString() => "100.(91823)" **/ toString: function(e) { var t = this.n, n = this.d; if (isNaN(t) || isNaN(n)) return "NaN"; e = e || 15; var u = Vg(t, n), r = Rg(t, n, u), i = this.s < 0 ? "-" : ""; if (i += t / n | 0, t %= n, t *= 10, t && (i += "."), u) { for (var s = r; s--; ) i += t / n | 0, t %= n, t *= 10; i += "("; for (var s = u; s--; ) i += t / n | 0, t %= n, t *= 10; i += ")"; } else for (var s = e; t && s--; ) i += t / n | 0, t %= n, t *= 10; return i; } }; const Li = "strudel.log"; let Gg = 1e3, ia, sa; function je(e, t, n = {}) { let u = performance.now(); ia === e && u - sa < Gg || (ia = e, sa = u, console.log(`%c${e}`, "background-color: black;color:white;border-radius:15px"), typeof document < "u" && typeof CustomEvent < "u" && document.dispatchEvent( new CustomEvent(Li, { detail: { message: e, type: t, data: n } }) )); } je.key = Li; const Og = (e) => /^[a-gA-G][#bs]*[0-9]$/.test(e), Vn = (e) => /^[a-gA-G][#bsf]*[0-9]?$/.test(e), Qa = (e) => { if (typeof e != "string") return []; const [t, n = "", u] = e.match(/^([a-gA-G])([#bsf]*)([0-9]*)$/)?.slice(1) || []; return t ? [t, n, u ? Number(u) : void 0] : []; }, Wg = { c: 0, d: 2, e: 4, f: 5, g: 7, a: 9, b: 11 }, $g = { "#": 1, b: -1, s: 1, f: -1 }, bn = (e, t = 3) => { const [n, u, r = t] = Qa(e); if (!n) throw new Error('not a note: "' + e + '"'); const i = Wg[n.toLowerCase()], s = u?.split("").reduce((a, l) => a + $g[l], 0) || 0; return (Number(r) + 1) * 12 + i + s; }, _n = (e) => Math.pow(2, (e - 69) / 12) * 440, Gi = (e) => 12 * Math.log(e / 440) / Math.LN2 + 69, zg = (e, t) => { if (typeof e != "object") throw new Error("valueToMidi: expected object value"); let { freq: n, note: u } = e; if (typeof n == "number") return Gi(n); if (typeof u == "string") return bn(u); if (typeof u == "number") return u; if (!t) throw new Error("valueToMidi: expected freq or note to be set"); return t; }, qg = (e, t) => (e - t) * 1e3, eo = (e) => _n(typeof e == "number" ? e : bn(e)), jg = ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"], Xg = (e) => { const t = Math.floor(e / 12) - 1; return jg[e % 12] + t; }, bt = (e, t) => (e % t + t) % t; function to(e, t = 0) { return isNaN(Number(e)) ? (je(`"${e}" is not a number, falling back to ${t}`, "warning"), t) : e; } const Zg = (e, t) => bt(Math.round(to(e ?? 0, 0)), t), Hg = (e) => { let { value: t, context: n } = e, u = t; if (typeof u == "object" && !Array.isArray(u) && (u = u.note || u.n || u.value, u === void 0)) throw new Error(`cannot find a playable note for ${JSON.stringify(t)}`); if (typeof u == "number" && n.type !== "frequency") u = _n(e.value); else if (typeof u == "number" && n.type === "frequency") u = e.value; else if (typeof u != "string" || !Vn(u)) throw new Error("not a note: " + JSON.stringify(u)); return u; }, no = (e) => { let { value: t, context: n } = e; if (typeof t == "object") return t.freq ? t.freq : eo(t.note || t.n || t.value); if (typeof t == "number" && n.type !== "frequency") t = _n(e.value); else if (typeof t == "string" && Vn(t)) t = _n(bn(e.value)); else if (typeof t != "number") throw new Error("not a note or frequency: " + t); return t; }, uo = (e, t) => e.slice(t).concat(e.slice(0, t)), ro = (...e) => e.reduce( (t, n) => (...u) => t(n(...u)), (t) => t ), Kg = (...e) => ro(...e.reverse()), ru = (e) => e.filter((t) => t != null), tn = (e) => [].concat(...e), Zn = (e) => e, Yg = (e, t) => e, Oi = (e, t) => Array.from({ length: t - e + 1 }, (n, u) => u + e); function ye(e, t, n = e.length) { const u = function r(...i) { if (i.length >= n) return e.apply(this, i); { const s = function(...a) { return r.apply(this, i.concat(a)); }; return t && t(s, i), s; } }; return t && t(u, []), u; } function Wi(e) { const t = Number(e); if (!isNaN(t)) return t; if (Vn(e)) return bn(e); throw new Error(`cannot parse as numeral: "${e}"`); } function $i(e, t) { return (...n) => e(...n.map(t)); } function Dt(e) { return $i(e, Wi); } function io(e) { const t = Number(e); if (!isNaN(t)) return t; const n = { pi: Math.PI, w: 1, h: 0.5, q: 0.25, e: 0.125, s: 0.0625, t: 1 / 3, f: 0.2, x: 1 / 6 }[e]; if (typeof n < "u") return n; throw new Error(`cannot parse as fractional: "${e}"`); } const Jg = (e) => $i(e, io), zi = function(e, t) { return [t.slice(0, e), t.slice(e)]; }, qi = (e, t, n) => t.map((u, r) => e(u, n[r])), so = function(e) { const t = []; for (let n = 0; n < e.length - 1; ++n) t.push([e[n], e[n + 1]]); return t; }, ji = (e, t, n) => Math.min(Math.max(e, t), n), Ug = ["Do", "Reb", "Re", "Mib", "Mi", "Fa", "Solb", "Sol", "Lab", "La", "Sib", "Si"], Qg = [ "Sa", "Re", "Ga", "Ma", "Pa", "Dha", "Ni" ], eA = ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Hb", "H"], tA = [ "Ni", "Pab", "Pa", "Voub", "Vou", "Ga", "Dib", "Di", "Keb", "Ke", "Zob", "Zo" ], nA = [ "I", "Ro", "Ha", "Ni", "Ho", "He", "To" ], uA = ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"], rA = (e, t = "letters") => { const u = (t === "solfeggio" ? Ug : t === "indian" ? Qg : t === "german" ? eA : t === "byzantine" ? tA : t === "japanese" ? nA : uA)[e % 12], r = Math.floor(e / 12) - 1; return u + r; }; function iA(e) { var t = {}; return e.filter(function(n) { return t.hasOwn(n) ? !1 : t[n] = !0; }); } function sA(e) { return e.sort().filter(function(t, n, u) { return !n || t != u[n - 1]; }); } function ao(e) { return e.sort((t, n) => t.compare(n)).filter(function(t, n, u) { return !n || t.ne(u[n - 1]); }); } function oo(e) { const t = new TextEncoder().encode(e); return btoa(String.fromCharCode(...t)); } function co(e) { const t = new Uint8Array( atob(e).split("").map((u) => u.charCodeAt(0)) ); return new TextDecoder().decode(t); } function aA(e) { return encodeURIComponent(oo(e)); } function oA(e) { return co(decodeURIComponent(e)); } function lo(e, t) { return Array.isArray(e) ? e.map(t) : Object.fromEntries(Object.entries(e).map(([n, u], r) => [n, t(u, n, r)])); } Be.prototype.sam = function() { return this.floor(); }; Be.prototype.nextSam = function() { return this.sam().add(1); }; Be.prototype.wholeCycle = function() { return new qe(this.sam(), this.nextSam()); }; Be.prototype.cyclePos = function() { return this.sub(this.sam()); }; Be.prototype.lt = function(e) { return this.compare(e) < 0; }; Be.prototype.gt = function(e) { return this.compare(e) > 0; }; Be.prototype.lte = function(e) { return this.compare(e) <= 0; }; Be.prototype.gte = function(e) { return this.compare(e) >= 0; }; Be.prototype.eq = function(e) { return this.compare(e) == 0; }; Be.prototype.ne = function(e) { return this.compare(e) != 0; }; Be.prototype.max = function(e) { return this.gt(e) ? this : e; }; Be.prototype.maximum = function(...e) { return e = e.map((t) => new Be(t)), e.reduce((t, n) => n.max(t), this); }; Be.prototype.min = function(e) { return this.lt(e) ? this : e; }; Be.prototype.mulmaybe = function(e) { return e !== void 0 ? this.mul(e) : void 0; }; Be.prototype.divmaybe = function(e) { return e !== void 0 ? this.div(e) : void 0; }; Be.prototype.addmaybe = function(e) { return e !== void 0 ? this.add(e) : void 0; }; Be.prototype.submaybe = function(e) { return e !== void 0 ? this.sub(e) : void 0; }; Be.prototype.show = function() { return this.s * this.n + "/" + this.d; }; Be.prototype.or = function(e) { return this.eq(0) ? e : this; }; const X = (e) => Be(e), cA = (...e) => { if (e = ru(e), e.length !== 0) return e.reduce((t, n) => t.gcd(n), X(1)); }, nn = (...e) => { if (e = ru(e), e.length !== 0) return e.reduce( (t, n) => t === void 0 || n === void 0 ? void 0 : t.lcm(n), X(1) ); }; X._original = Be; class qe { constructor(t, n) { this.begin = X(t), this.end = X(n); } get spanCycles() { const t = []; var n = this.begin; const u = this.end, r = u.sam(); if (n.equals(u)) return [new qe(n, u)]; for (; u.gt(n); ) { if (n.sam().equals(r)) { t.push(new qe(n, this.end)); break; } const i = n.nextSam(); t.push(new qe(n, i)), n = i; } return t; } get duration() { return this.end.sub(this.begin); } cycleArc() { const t = this.begin.cyclePos(), n = t.add(this.duration); return new qe(t, n); } withTime(t) { return new qe(t(this.begin), t(this.end)); } withEnd(t) { return new qe(this.begin, t(this.end)); } withCycle(t) { const n = this.begin.sam(), u = n.add(t(this.begin.sub(n))), r = n.add(t(this.end.sub(n))); return new qe(u, r); } intersection(t) { const n = this.begin.max(t.begin), u = this.end.min(t.end); if (!n.gt(u) && !(n.equals(u) && (n.equals(this.end) && this.begin.lt(this.end) || n.equals(t.end) && t.begin.lt(t.end)))) return new qe(n, u); } intersection_e(t) { const n = this.intersection(t); if (n == null) throw "TimeSpans do not intersect"; return n; } midpoint() { return this.begin.add(this.duration.div(X(2))); } equals(t) { return this.begin.equals(t.begin) && this.end.equals(t.end); } show() { return this.begin.show() + " → " + this.end.show(); } } class $e { /* Event class, representing a value active during the timespan 'part'. This might be a fragment of an event, in which case the timespan will be smaller than the 'whole' timespan, otherwise the two timespans will be the same. The 'part' must never extend outside of the 'whole'. If the event represents a continuously changing value then the whole will be returned as None, in which case the given value will have been sampled from the point halfway between the start and end of the 'part' timespan. The context is to store a list of source code locations causing the event. The word 'Event' is more or less a reserved word in javascript, hence this class is named called 'Hap'. */ constructor(t, n, u, r = {}, i = !1) { this.whole = t, this.part = n, this.value = u, this.context = r, this.stateful = i, i && console.assert(typeof this.value == "function", "Stateful values must be functions"); } get duration() { let t; return typeof this.value?.duration == "number" ? t = X(this.value.duration) : t = this.whole.end.sub(this.whole.begin), typeof this.value?.clip == "number" ? t.mul(this.value.clip) : t; } get endClipped() { return this.whole.begin.add(this.duration); } isActive(t) { return this.whole.begin <= t && this.endClipped >= t; } isInPast(t) { return t > this.endClipped; } isInNearPast(t, n) { return n - t <= this.endClipped; } isInFuture(t) { return t < this.whole.begin; } isInNearFuture(t, n) { return n < this.whole.begin && n > this.whole.begin - t; } isWithinTime(t, n) { return this.whole.begin <= n && this.endClipped >= t; } wholeOrPart() { return this.whole ? this.whole : this.part; } withSpan(t) { const n = this.whole ? t(this.whole) : void 0; return new $e(n, t(this.part), this.value, this.context); } withValue(t) { return new $e(this.whole, this.part, t(this.value), this.context); } hasOnset() { return this.whole != null && this.whole.begin.equals(this.part.begin); } hasTag(t) { return this.context.tags?.includes(t); } resolveState(t) { if (this.stateful && this.hasOnset()) { console.log("stateful"); const n = this.value, [u, r] = n(t); return [u, new $e(this.whole, this.part, r, this.context, !1)]; } return [t, this]; } spanEquals(t) { return this.whole == null && t.whole == null || this.whole.equals(t.whole); } equals(t) { return this.spanEquals(t) && this.part.equals(t.part) && // TODO would == be better ?? this.value === t.value; } show(t = !1) { const n = typeof this.value == "object" ? t ? JSON.stringify(this.value).slice(1, -1).replaceAll('"', "").replaceAll(",", " ") : JSON.stringify(this.value) : this.value; var u = ""; if (this.whole == null) u = "~" + this.part.show; else { var r = this.whole.begin.equals(this.part.begin) && this.whole.end.equals(this.part.end); this.whole.begin.equals(this.part.begin) || (u = this.whole.begin.show() + " ⇜ "), r || (u += "("), u += this.part.show(), r || (u += ")"), this.whole.end.equals(this.part.end) || (u += " ⇝ " + this.whole.end.show()); } return "[ " + u + " | " + n + " ]"; } showWhole(t = !1) { return `${this.whole == null ? "~" : this.whole.show()}: ${typeof this.value == "object" ? t ? JSON.stringify(this.value).slice(1, -1).replaceAll('"', "").replaceAll(",", " ") : JSON.stringify(this.value) : this.value}`; } combineContext(t) { const n = this; return { ...n.context, ...t.context, locations: (n.context.locations || []).concat(t.context.locations || []) }; } setContext(t) { return new $e(this.whole, this.part, this.value, t); } ensureObjectValue() { if (typeof this.value != "object") throw new Error( `expected hap.value to be an object, but got "${this.value}". Hint: append .note() or .s() to the end`, "error" ); } } class Un { constructor(t, n = {}) { this.span = t, this.controls = n; } // Returns new State with different span setSpan(t) { return new Un(t, this.controls); } withSpan(t) { return this.setSpan(t(this.span)); } // Returns new State with different controls setControls(t) { return new Un(this.span, t); } } function lA(e, t, n) { if (t?.value !== void 0 && Object.keys(t).length === 1) return je("[warn]: Can't do arithmetic on control pattern."), e; const u = Object.keys(e).filter((r) => Object.keys(t).includes(r)); return Object.assign({}, e, t, Object.fromEntries(u.map((r) => [r, n(e[r], t[r])]))); } ye((e, t) => e * t); ye((e, t) => t.map(e)); function fo(e, t = 60) { let n = 0, u = X(0), r = [""], i = ""; for (; r[0].length < t; ) { const s = e.queryArc(n, n + 1), a = s.filter((f) => f.hasOnset()).map((f) => f.duration), l = cA(...a), h = l.inverse(); r = r.map((f) => f + "|"), i += "|"; for (let f = 0; f < h; f++) { const [m, p] = [u, u.add(l)], D = s.filter((E) => E.whole.begin.lte(m) && E.whole.end.gte(p)), y = D.length - r.length; y > 0 && (r = r.concat(Array(y).fill(i))), r = r.map((E, B) => { const S = D[B]; if (S) { const O = S.whole.begin.eq(m) ? "" + S.value : "-"; return E + O; } return E + "."; }), i += ".", u = u.add(l); } n++; } return r.join(` `); } let Fi, Rt = !0; const fA = function(e) { Rt = !!e; }, ho = (e) => Fi = e; let ue = class Ue { /** * Create a pattern. As an end user, you will most likely not create a Pattern directly. * * @param {function} query - The function that maps a `State` to an array of `Hap`. * @noAutocomplete */ constructor(t, n = void 0) { this.query = t, this._Pattern = !0, this.tactus = n; } get tactus() { return this.__tactus; } set tactus(t) { this.__tactus = t === void 0 ? void 0 : X(t); } setTactus(t) { return this.tactus = t, this; } withTactus(t) { return Rt ? new Ue(this.query, this.tactus === void 0 ? void 0 : t(this.tactus)) : this; } get hasTactus() { return this.tactus !== void 0; } ////////////////////////////////////////////////////////////////////// // Haskell-style functor, applicative and monadic operations /** * Returns a new pattern, with the function applied to the value of * each hap. It has the alias `fmap`. * @synonyms fmap * @param {Function} func to to apply to the value * @returns Pattern * @example * "0 1 2".withValue(v => v + 10).log() */ withValue(t) { const n = new Ue((u) => this.query(u).map((r) => r.withValue(t))); return n.tactus = this.tactus, n; } /** * see `withValue` * @noAutocomplete */ fmap(t) { return this.withValue(t); } /** * Assumes 'this' is a pattern of functions, and given a function to * resolve wholes, applies a given pattern of values to that * pattern of functions. * @param {Function} whole_func * @param {Function} func * @noAutocomplete * @returns Pattern */ appWhole(t, n) { const u = this, r = function(i) { const s = u.query(i), a = n.query(i), l = function(h, f) { const m = h.part.intersection(f.part); if (m != null) return new $e( t(h.whole, f.whole), m, h.value(f.value), f.combineContext(h) ); }; return tn( s.map((h) => ru(a.map((f) => l(h, f)))) ); }; return new Ue(r); } /** * When this method is called on a pattern of functions, it matches its haps * with those in the given pattern of values. A new pattern is returned, with * each matching value applied to the corresponding function. * * In this `_appBoth` variant, where timespans of the function and value haps * are not the same but do intersect, the resulting hap has a timespan of the * intersection. This applies to both the part and the whole timespan. * @param {Pattern} pat_val * @noAutocomplete * @returns Pattern */ appBoth(t) { const n = this, u = function(i, s) { if (!(i == null || s == null)) return i.intersection_e(s); }, r = n.appWhole(u, t); return Rt && (r.tactus = nn(t.tactus, n.tactus)), r; } /** * As with `appBoth`, but the `whole` timespan is not the intersection, * but the timespan from the function of patterns that this method is called * on. In practice, this means that the pattern structure, including onsets, * are preserved from the pattern of functions (often referred to as the left * hand or inner pattern). * @param {Pattern} pat_val * @noAutocomplete * @returns Pattern */ appLeft(t) { const n = this, u = function(i) { const s = []; for (const a of n.query(i)) { const l = t.query(i.setSpan(a.wholeOrPart())); for (const h of l) { const f = a.whole, m = a.part.intersection(h.part); if (m) { const p = a.value(h.value), D = h.combineContext(a), y = new $e(f, m, p, D); s.push(y); } } } return s; }, r = new Ue(u); return r.tactus = this.tactus, r; } /** * As with `appLeft`, but `whole` timespans are instead taken from the * pattern of values, i.e. structure is preserved from the right hand/outer * pattern. * @param {Pattern} pat_val * @noAutocomplete * @returns Pattern */ appRight(t) { const n = this, u = function(i) { const s = []; for (const a of t.query(i)) { const l = n.query(i.setSpan(a.wholeOrPart())); for (const h of l) { const f = a.whole, m = h.part.intersection(a.part); if (m) { const p = h.value(a.value), D = a.combineContext(h), y = new $e(f, m, p, D); s.push(y); } } } return s; }, r = new Ue(u); return r.tactus = t.tactus, r; } bindWhole(t, n) { const u = this, r = function(i) { const s = function(l, h) { return new $e( t(l.whole, h.whole), h.part, h.value, Object.assign({}, l.context, h.context, { locations: (l.context.locations || []).concat(h.context.locations || []) }) ); }, a = function(l) { return n(l.value).query(i.setSpan(l.part)).map((h) => s(l, h)); }; return tn(u.query(i).map((l) => a(l))); }; return new Ue(r); } bind(t) { const n = function(u, r) { if (!(u == null || r == null)) return u.intersection_e(r); }; return this.bindWhole(n, t); } join() { return this.bind(Zn); } outerBind(t) { return this.bindWhole((n) => n, t).setTactus(this.tactus); } outerJoin() { return this.outerBind(Zn); } innerBind(t) { return this.bindWhole((n, u) => u, t); } innerJoin() { return this.innerBind(Zn); } // Flatterns patterns of patterns, by retriggering/resetting inner patterns at onsets of outer pattern haps resetJoin(t = !1) { const n = this; return new Ue((u) => n.discreteOnly().query(u).map((r) => r.value.late(t ? r.whole.begin : r.whole.begin.cyclePos()).query(u).map( (i) => new $e( // Supports continuous haps in the inner pattern i.whole ? i.whole.intersection(r.whole) : void 0, i.part.intersection(r.part), i.value ).setContext(r.combineContext(i)) ).filter((i) => i.part)).flat()); } restartJoin() { return this.resetJoin(!0); } // Like the other joins above, joins a pattern of patterns of values, into a flatter // pattern of values. In this case it takes whole cycles of the inner pattern to fit each event // in the outer pattern. squeezeJoin() { const t = this; function n(u) { const r = t.discreteOnly().query(u); function i(a) { const h = a.value._focusSpan(a.wholeOrPart()).query(u.setSpan(a.part)); function f(m, p) { let D; if (p.whole && m.whole && (D = p.whole.intersection(m.whole), !D)) return; const y = p.part.intersection(m.part); if (!y) return; const E = p.combineContext(m); return new $e(D, y, p.value, E); } return h.map((m) => f(a, m)); } return tn(r.map(i)).filter((a) => a); } return new Ue(n); } squeezeBind(t) { return this.fmap(t).squeezeJoin(); } ////////////////////////////////////////////////////////////////////// // Utility methods mainly for internal use /** * Query haps inside the given time span. * * @param {Fraction | number} begin from time * @param {Fraction | number} end to time * @returns Hap[] * @example * const pattern = sequence('a', ['b', 'c']) * const haps = pattern.queryArc(0, 1) * console.log(haps) * silence * @noAutocomplete */ queryArc(t, n, u = {}) { try { return this.query(new Un(new qe(t, n), u)); } catch (r) { return je(`[query]: ${r.message}`, "error"), []; } } /** * Returns a new pattern, with queries split at cycle boundaries. This makes * some calculations easier to express, as all haps are then constrained to * happen within a cycle. * @returns Pattern * @noAutocomplete */ splitQueries() { const t = this, n = (u) => tn(u.span.spanCycles.map((r) => t.query(u.setSpan(r)))); return new Ue(n); } /** * Returns a new pattern, where the given function is applied to the query * timespan before passing it to the original pattern. * @param {Function} func the function to apply * @returns Pattern * @noAutocomplete */ withQuerySpan(t) { return new Ue((n) => this.query(n.withSpan(t))); } withQuerySpanMaybe(t) { const n = this; return new Ue((u) => { const r = u.withSpan(t); return r.span ? n.query(r) : []; }); } /** * As with `withQuerySpan`, but the function is applied to both the * begin and end time of the query timespan. * @param {Function} func the function to apply * @returns Pattern * @noAutocomplete */ withQueryTime(t) { return new Ue((n) => this.query(n.withSpan((u) => u.withTime(t)))); } /** * Similar to `withQuerySpan`, but the function is applied to the timespans * of all haps returned by pattern queries (both `part` timespans, and where * present, `whole` timespans). * @param {Function} func * @returns Pattern * @noAutocomplete */ withHapSpan(t) { return new Ue((n) => this.query(n).map((u) => u.withSpan(t))); } /** * As with `withHapSpan`, but the function is applied to both the * begin and end time of the hap timespans. * @param {Function} func the function to apply * @returns Pattern * @noAutocomplete */ withHapTime(t) { return this.withHapSpan((n) => n.withTime(t)); } /** * Returns a new pattern with the given function applied to the list of haps returned by every query. * @param {Function} func * @returns Pattern * @noAutocomplete */ withHaps(t) { const n = new Ue((u) => t(this.query(u), u)); return n.tactus = this.tactus, n; } /** * As with `withHaps`, but applies the function to every hap, rather than every list of haps. * @param {Function} func * @returns Pattern * @noAutocomplete */ withHap(t) { return this.withHaps((n) => n.map(t)); } /** * Returns a new pattern with the context field set to every hap set to the given value. * @param {*} context * @returns Pattern * @noAutocomplete */ setContext(t) { return this.withHap((n) => n.setContext(t)); } /** * Returns a new pattern with the given function applied to the context field of every hap. * @param {Function} func * @returns Pattern * @noAutocomplete */ withContext(t) { const n = this.withHap((u) => u.setContext(t(u.context))); return this.__pure !== void 0 && (n.__pure = this.__pure, n.__pure_loc = this.__pure_loc), n; } /** * Returns a new pattern with the context field of every hap set to an empty object. * @returns Pattern * @noAutocomplete */ stripContext() { return this.withHap((t) => t.setContext({})); } /** * Returns a new pattern with the given location information added to the * context of every hap. * @param {Number} start start offset * @param {Number} end end offset * @returns Pattern * @noAutocomplete */ withLoc(t, n) { const u = { start: t, end: n }, r = this.withContext((i) => { const s = (i.locations || []).concat([u]); return { ...i, locations: s }; }); return this.__pure && (r.__pure = this.__pure, r.__pure_loc = u), r; } /** * Returns a new Pattern, which only returns haps that meet the given test. * @param {Function} hap_test - a function which returns false for haps to be removed from the pattern * @returns Pattern * @noAutocomplete */ filterHaps(t) { return new Ue((n) => this.query(n).filter(t)); } /** * As with `filterHaps`, but the function is applied to values * inside haps. * @param {Function} value_test * @returns Pattern * @noAutocomplete */ filterValues(t) { return new Ue((n) => this.query(n).filter((u) => t(u.value))).setTactus(this.tactus); } /** * Returns a new pattern, with haps containing undefined values removed from * query results. * @returns Pattern * @noAutocomplete */ removeUndefineds() { return this.filterValues((t) => t != null); } /** * Returns a new pattern, with all haps without onsets filtered out. A hap * with an onset is one with a `whole` timespan that begins at the same time * as its `part` timespan. * @returns Pattern * @noAutocomplete */ onsetsOnly() { return this.filterHaps((t) => t.hasOnset()); } /** * Returns a new pattern, with 'continuous' haps (those without 'whole' * timespans) removed from query results. * @returns Pattern * @noAutocomplete */ discreteOnly() { return this.filterHaps((t) => t.whole); } /** * Combines adjacent haps with the same value and whole. Only * intended for use in tests. * @noAutocomplete */ defragmentHaps() { return this.discreteOnly().withHaps((n) => { const u = []; for (var r = 0; r < n.length; ++r) { for (var i = !0, s = n[r]; i; ) { const h = JSON.stringify(n[r].value); for (var a = !1, l = r + 1; l < n.length; l++) { const f = n[l]; if (s.whole.equals(f.whole)) { if (s.part.begin.eq(f.part.end)) { if (h === JSON.stringify(f.value)) { s = new $e(s.whole, new qe(f.part.begin, s.part.end), s.value), n.splice(l, 1), a = !0; break; } } else if (f.part.begin.eq(s.part.end) && h == JSON.stringify(f.value)) { s = new $e(s.whole, new qe(s.part.begin, f.part.end), s.value), n.splice(l, 1), a = !0; break; } } } i = a; } u.push(s); } return u; }); } /** * Queries the pattern for the first cycle, returning Haps. Mainly of use when * debugging a pattern. * @param {Boolean} with_context - set to true, otherwise the context field * will be stripped from the resulting haps. * @returns [Hap] * @noAutocomplete */ firstCycle(t = !1) { var n = this; return t || (n = n.stripContext()), n.query(new Un(new qe(X(0), X(1)))); } /** * Accessor for a list of values returned by querying the first cycle. * @noAutocomplete */ get firstCycleValues() { return this.firstCycle().map((t) => t.value); } /** * More human-readable version of the `firstCycleValues` accessor. * @noAutocomplete */ get showFirstCycle() { return this.firstCycle().map( (t) => `${t.value}: ${t.whole.begin.toFraction()} - ${t.whole.end.toFraction()}` ); } /** * Returns a new pattern, which returns haps sorted in temporal order. Mainly * of use when comparing two patterns for equality, in tests. * @returns Pattern * @noAutocomplete */ sortHapsByPart() { return this.withHaps( (t) => t.sort( (n, u) => n.part.begin.sub(u.part.begin).or(n.part.end.sub(u.part.end)).or(n.whole.begin.sub(u.whole.begin).or(n.whole.end.sub(u.whole.end))) ) ); } asNumber() { return this.fmap(Wi); } ////////////////////////////////////////////////////////////////////// // Operators - see 'make composers' later.. _opIn(t, n) { return this.fmap(n).appLeft(U(t)); } _opOut(t, n) { return this.fmap(n).appRight(U(t)); } _opMix(t, n) { return this.fmap(n).appBoth(U(t)); } _opSqueeze(t, n) { const u = U(t); return this.fmap((r) => u.fmap((i) => n(r)(i))).squeezeJoin(); } _opSqueezeOut(t, n) { const u = this; return U(t).fmap((i) => u.fmap((s) => n(s)(i))).squeezeJoin(); } _opReset(t, n) { return U(t).fmap((r) => this.fmap((i) => n(i)(r))).resetJoin(); } _opRestart(t, n) { return U(t).fmap((r) => this.fmap((i) => n(i)(r))).restartJoin(); } ////////////////////////////////////////////////////////////////////// // End-user methods. // Those beginning with an underscore (_) are 'patternified', // i.e. versions are created without the underscore, that are // magically transformed to accept patterns for all their arguments. ////////////////////////////////////////////////////////////////////// // Methods without corresponding toplevel functions /** * Layers the result of the given function(s). Like `superimpose`, but without the original pattern: * @name layer * @memberof Pattern * @synonyms apply * @returns Pattern * @example * "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*8" * .layer(x=>x.add("0,2")) * .scale('C minor').note() */ layer(...t) { return Le(...t.map((n) => n(this))); } /** * Superimposes the result of the given function(s) on top of the original pattern: * @name superimpose * @memberof Pattern * @returns Pattern * @example * "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*8" * .superimpose(x=>x.add(2)) * .scale('C minor').note() */ superimpose(...t) { return this.stack(...t.map((n) => n(this))); } ////////////////////////////////////////////////////////////////////// // Multi-pattern functions /** * Stacks the given pattern(s) to the current pattern. * @name stack * @memberof Pattern * @example * s("hh*4").stack( * note("c4(5,8)") * ) */ stack(...t) { return Le(this, ...t); } sequence(...t) { return Mt(this, ...t); } /** * Appends the given pattern(s) to the current pattern. * @name seq * @memberof Pattern * @synonyms sequence, fastcat * @example * s("hh*4").seq( * note("c4(5,8)") * ) */ seq(...t) { return Mt(this, ...t); } /** * Appends the given pattern(s) to the next cycle. * @name cat * @memberof Pattern * @synonyms slowcat * @example * s("hh*4").cat( * note("c4(5,8)") * ) */ cat(...t) { return Ao(this, ...t); } fastcat(...t) { return Pt(this, ...t); } slowcat(...t) { return Rn(this, ...t); } ////////////////////////////////////////////////////////////////////// // Context methods - ones that deal with metadata onTrigger(t, n = !0) { return this.withHap( (u) => u.setContext({ ...u.context, onTrigger: (...r) => { u.context.onTrigger?.(...r), t(...r); }, // if dominantTrigger is set to true, the default output (webaudio) will be disabled // when using multiple triggers, you cannot flip this flag to false again! // example: x.csound('CooLSynth').log() as well as x.log().csound('CooLSynth') should work the same dominantTrigger: u.context.dominantTrigger || n }) ); } log(t = (u, r) => `[hap] ${r.showWhole(!0)}`, n = (u, r) => ({ hap: r })) { return this.onTrigger((...u) => { je(t(...u), void 0, n(...u)); }, !1); } logValues(t = Zn) { return this.log((n, u) => t(u.value)); } ////////////////////////////////////////////////////////////////////// // Visualisation drawLine() { return console.log(fo(this)), this; } }; function hA(e, t) { let n = []; return t.forEach((u) => { const r = n.findIndex(([i]) => e(u, i)); r === -1 ? n.push([u]) : n[r].push(u); }), n; } const pA = (e, t) => e.spanEquals(t); ue.prototype.collect = function() { return this.withHaps( (e) => hA(pA, e).map((t) => new $e(t[0].whole, t[0].part, t, {})) ); }; ue.prototype.arpWith = function(e) { return this.collect().fmap((t) => U(e(t))).innerJoin().withHap((t) => new $e(t.whole, t.part, t.value.value, t.combineContext(t.value))); }; ue.prototype.arp = function(e) { return this.arpWith((t) => e.fmap((n) => t[n % t.length])); }; function Qu(e) { return !Array.isArray(e) && typeof e == "object"; } function dA(e, t, n) { return Qu(e) || Qu(t) ? (Qu(e) || (e = { value: e }), Qu(t) || (t = { value: t }), lA(e, t, n)) : n(e, t); } (function() { const e = { set: [(n, u) => u], keep: [(n) => n], keepif: [(n, u) => u ? n : void 0], // numerical functions /** * * Assumes a pattern of numbers. Adds the given number to each item in the pattern. * @name add * @memberof Pattern * @example * // Here, the triad 0, 2, 4 is shifted by different amounts * n("0 2 4".add("<0 3 4 0>")).scale("C:major") * // Without add, the equivalent would be: * // n("<[0 2 4] [3 5 7] [4 6 8] [0 2 4]>").scale("C:major") * @example * // You can also use add with notes: * note("c3 e3 g3".add("<0 5 7 0>")) * // Behind the scenes, the notes are converted to midi numbers: * // note("48 52 55".add("<0 5 7 0>")) */ add: [Dt((n, u) => n + u)], // support string concatenation /** * * Like add, but the given numbers are subtracted. * @name sub * @memberof Pattern * @example * n("0 2 4".sub("<0 1 2 3>")).scale("C4:minor") * // See add for more information. */ sub: [Dt((n, u) => n - u)], /** * * Multiplies each number by the given factor. * @name mul * @memberof Pattern * @example * "<1 1.5 [1.66, <2 2.33>]>*4".mul(150).freq() */ mul: [Dt((n, u) => n * u)], /** * * Divides each number by the given factor. * @name div * @memberof Pattern */ div: [Dt((n, u) => n / u)], mod: [Dt(bt)], pow: [Dt(Math.pow)], band: [Dt((n, u) => n & u)], bor: [Dt((n, u) => n | u)], bxor: [Dt((n, u) => n ^ u)], blshift: [Dt((n, u) => n << u)], brshift: [Dt((n, u) => n >> u)], // TODO - force numerical comparison if both look like numbers? lt: [(n, u) => n < u], gt: [(n, u) => n > u], lte: [(n, u) => n <= u], gte: [(n, u) => n >= u], eq: [(n, u) => n == u], eqt: [(n, u) => n === u], ne: [(n, u) => n != u], net: [(n, u) => n !== u], and: [(n, u) => n && u], or: [(n, u) => n || u], // bitwise ops func: [(n, u) => u(n)] }, t = ["In", "Out", "Mix", "Squeeze", "SqueezeOut", "Reset", "Restart"]; for (const [n, [u, r]] of Object.entries(e)) { ue.prototype["_" + n] = function(i) { return this.fmap((s) => u(s, i)); }, Object.defineProperty(ue.prototype, n, { // a getter that returns a function, so 'pat' can be // accessed by closures that are methods of that function.. get: function() { const i = this, s = (...a) => i[n].in(...a); for (const a of t) s[a.toLowerCase()] = function(...l) { var h = i; l = Mt(l), r && (h = r(h), l = r(l)); var f; return n === "keepif" ? (f = h["_op" + a](l, (m) => (p) => u(m, p)), f = f.removeUndefineds()) : f = h["_op" + a](l, (m) => (p) => dA(m, p, u)), f; }; return s.squeezein = s.squeeze, s; } }); for (const i of t) ue.prototype[i.toLowerCase()] = function(...s) { return this.set[i.toLowerCase()](s); }; } ue.prototype.struct = function(...n) { return this.keepif.out(...n); }, ue.prototype.structAll = function(...n) { return this.keep.out(...n); }, ue.prototype.mask = function(...n) { return this.keepif.in(...n); }, ue.prototype.maskAll = function(...n) { return this.keep.in(...n); }, ue.prototype.reset = function(...n) { return this.keepif.reset(...n); }, ue.prototype.resetAll = function(...n) { return this.keep.reset(...n); }, ue.prototype.restart = function(...n) { return this.keepif.restart(...n); }, ue.prototype.restartAll = function(...n) { return this.keep.restart(...n); }; })(); const mA = Le, gA = Le, AA = Ui, iu = (e) => new ue(() => [], e), Pe = iu(1), yt = iu(0); function tt(e) { function t(u) { return u.span.sp