diff --git a/index.html b/index.html new file mode 100644 index 00000000..1baabcb6 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + Bingo + + + + + + + + diff --git a/js/package.json b/js/package.json new file mode 100644 index 00000000..e69de29b diff --git a/js/strudel.js b/js/strudel.js new file mode 100644 index 00000000..b01bea88 --- /dev/null +++ b/js/strudel.js @@ -0,0 +1,138 @@ +import Fraction from 'fraction.js' + +Fraction.prototype.sam = function() { + return Fraction(Math.floor(this)) +} + +Fraction.prototype.lt = function(other) { + return this.compare(other) < 0 +} + +Fraction.prototype.lte = function(other) { + return this.compare(other) <= 0 +} + +Fraction.prototype.gte = function(other) { + return this.compare(other) >= 0 +} + +class TimeSpan { + constructor(begin, end) { + this.begin = Fraction(begin) + this.end = Fraction(end) + } + + get spanCycles() { + var spans = [] + var begin = this.begin + var end = this.end + var end_sam = end.sam() + + while (end.gt(begin)) { + // If begin and end are in the same cycle, we're done. + if (begin.sam() == end_sam) { + spans.push(new TimeSpan(begin, this.end)) + break + } + // add a timespan up to the next sam + var next_begin = begin.next_sam() + spans.push(TimeSpan(begin, next_begin)) + + // continue with the next cycle + begin = next_begin + } + return(spans) + } + + withTime(func_time) { + // Applies given function to both the begin and end time value of the timespan""" + return(new TimeSpan(func_time(this.begin), func_time(this.end))) + } + + intersection(other) { + // Intersection of two timespans, returns None if they don't intersect. + intersect_begin = Math.max(this.begin, other.begin) + intersect_end = Math.min(this.end, other.end) + + if (intersect_begin.gt(intersect_end)) { + return(undefined) + } + if (intersect_begin.equals(intersect_end)) { + // Zero-width (point) intersection - doesn't intersect if it's at the end of a + // non-zero-width timespan. + if (intersect_begin.equals(this.end) && this.begin.lt(this.end)) { + return(undefined) + } + if (intersect_begin.equals(other.end) && other.begin.lt(other.end)) { + return(undefined) + } + } + return(new TimeSpan(intersect_begin, intersect_end)) + } + + intersection_e(other) { + // Like 'sect', but raises an exception if the timespans don't intersect. + result = this.intersection(other) + if (result == None) { + // TODO - raise exception + // raise ValueError(f'TimeSpan {self} and TimeSpan {other} do not intersect') + } + return(result) + } + + get midpoint() { + return(this.begin.add((this.end.sub(this.begin)).div(Fraction(2)))) + } + + equals(other) { + return this.begin.equals(other.begin) && this.end.equals(other.end) + } +} + +class Event { + + /* + 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. + */ + + constructor(whole, part, value) { + this.whole = whole + this.part = part + this.value = value + } + + with_span(func) { + // Returns a new event with the function f applies to the event timespan. + var whole = this.whole ? func(self.whole) : undefined + return new Event(whole, func(this.part), this.value) + } + + with_value(func) { + // Returns a new event with the function f applies to the event value. + return new Event(this.whole, this.part, func(this.value)) + } + + has_onset() { + // Test whether the event contains the onset, i.e that + // the beginning of the part is the same as that of the whole timespan.""" + return (this.whole != undefined) && (this.whole.begin == this.part.begin) + } +} + +window.bar = function() { + console.log("aha" + typeof(Fraction(3.5))); +} + +//console.log((new TimeSpan(3.5,4)).midpoint); + +var ts = new TimeSpan(0,4) +console.log((new Event(new TimeSpan(0,2), new TimeSpan(0,2), "aha")).has_onset()); +var x = new TimeSpan(0,4) +console.log(ts.equals(x)) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..abe13d7b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,53 @@ +{ + "name": "strudel", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "strudel", + "version": "1.0.0", + "license": "GPL-3.0-or-later", + "devDependencies": { + "fraction": "^0.2.0", + "fraction.js": "^4.1.2" + } + }, + "node_modules/fraction": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/fraction/-/fraction-0.2.0.tgz", + "integrity": "sha1-ZassG7bt+JmJ2B1Q8FJeX6J7Hkw=", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/fraction.js": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.2.tgz", + "integrity": "sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + } + }, + "dependencies": { + "fraction": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/fraction/-/fraction-0.2.0.tgz", + "integrity": "sha1-ZassG7bt+JmJ2B1Q8FJeX6J7Hkw=", + "dev": true + }, + "fraction.js": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.2.tgz", + "integrity": "sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..9d246a81 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "strudel", + "version": "1.0.0", + "description": "Experimental port of tidalcycles to javascript", + "main": "index.js", + "scripts": { + "test": "mocha" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/yaxu/strudel.git" + }, + "keywords": [ + "tidalcycles", + "strudel", + "pattern", + "livecoding", + "algorave" + ], + "author": "Alex McLean", + "license": "GPL-3.0-or-later", + "bugs": { + "url": "https://github.com/yaxu/strudel/issues" + }, + "homepage": "https://github.com/yaxu/strudel#readme", + "devDependencies": { + "fraction.js": "^4.1.2" + } +}