discombobulator 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/package.json +16 -0
  2. package/src/articulation.mjs +61 -0
  3. package/src/articulationset.mjs +20 -0
  4. package/src/chord.mjs +82 -0
  5. package/src/cross.mjs +133 -0
  6. package/src/disco.mjs +14 -0
  7. package/src/element.mjs +51 -0
  8. package/src/group.mjs +70 -0
  9. package/src/groupset.mjs +259 -0
  10. package/src/instrument.mjs +105 -0
  11. package/src/instrumentset.mjs +20 -0
  12. package/src/irregulargroupset.mjs +224 -0
  13. package/src/note.mjs +155 -0
  14. package/src/noteset.mjs +21 -0
  15. package/src/pattern.mjs +52 -0
  16. package/src/patternset.mjs +20 -0
  17. package/src/patternsource.mjs +21 -0
  18. package/src/permute.mjs +63 -0
  19. package/src/regulargroupset.mjs +120 -0
  20. package/src/schedule.mjs +88 -0
  21. package/src/schema.mjs +108 -0
  22. package/src/set.mjs +1159 -0
  23. package/src/test/articulation.mjs +66 -0
  24. package/src/test/articulationset.mjs +43 -0
  25. package/src/test/binaryoperator.mjs +210 -0
  26. package/src/test/chord.mjs +292 -0
  27. package/src/test/concolour.mjs +70 -0
  28. package/src/test/difference.mjs +492 -0
  29. package/src/test/element.mjs +66 -0
  30. package/src/test/group.mjs +79 -0
  31. package/src/test/groupset.mjs +43 -0
  32. package/src/test/instrument.mjs +66 -0
  33. package/src/test/instrumentset.mjs +43 -0
  34. package/src/test/intersection.mjs +493 -0
  35. package/src/test/irregulargroupset.mjs +461 -0
  36. package/src/test/note.mjs +367 -0
  37. package/src/test/noteset.mjs +43 -0
  38. package/src/test/operator.mjs +63 -0
  39. package/src/test/pattern.mjs +113 -0
  40. package/src/test/patternset.mjs +43 -0
  41. package/src/test/patternsource.mjs +43 -0
  42. package/src/test/regulargroupset.mjs +290 -0
  43. package/src/test/repl.mjs +63 -0
  44. package/src/test/replinit.mjs +134 -0
  45. package/src/test/rotation.mjs +753 -0
  46. package/src/test/schedule.mjs +275 -0
  47. package/src/test/schema.mjs +333 -0
  48. package/src/test/set.mjs +1081 -0
  49. package/src/test/symmetricdifference.mjs +495 -0
  50. package/src/test/test.mjs +208 -0
  51. package/src/test/testing.mjs +63 -0
  52. package/src/test/union.mjs +495 -0
  53. package/src/util.mjs +21 -0
@@ -0,0 +1,259 @@
1
+ // GroupSet.
2
+ //
3
+ // A group set is a set of groups, and allows patterns to be generated by
4
+ // permutating the group set with a pattern set.
5
+
6
+ import util from "util";
7
+
8
+ import { bitCount, factorial } from "./util.mjs";
9
+ import { permutations, permutationIndexGenerator } from "./permute.mjs";
10
+
11
+ import { Set } from "./set.mjs";
12
+ import { Group } from "./group.mjs";
13
+ import { Chord } from "./chord.mjs";
14
+ import { Pattern } from "./pattern.mjs";
15
+ import { PatternSet } from "./patternset.mjs";
16
+
17
+ export class GroupSet extends Set {
18
+ // Permute.
19
+ //
20
+ // Class to generate the patterns formed by permutating a group set
21
+ // with a pattern or pattern set. A permute is effectively an abstract
22
+ // pattern set.
23
+ static Permute = class extends PatternSet {
24
+ #groupSet = undefined;
25
+ #patternSet = undefined;
26
+ #allowRepeats = false;
27
+
28
+ // Construct a Permute.
29
+ //
30
+ // groupSet A GroupSet containing the groups to permute.
31
+ // patternSet A Pattern or a PatternSet to permute the groups with.
32
+ constructor(groupSet, patternSet, options = {}) {
33
+ const defaults = { allowRepeats: false };
34
+ options = { ...defaults, ...options, isAbstract: true };
35
+
36
+ super(options);
37
+
38
+ let patterns;
39
+
40
+ if (patternSet instanceof Pattern) {
41
+ // Promote a single Pattern to a PatternSet.
42
+ patterns = new PatternSet();
43
+ patterns.add(patternSet);
44
+ } else if (patternSet instanceof PatternSet) {
45
+ patterns = patternSet;
46
+ } else {
47
+ throw new Error("Invalid argument - permute requires a Pattern or PatternSet");
48
+ }
49
+
50
+ this.#groupSet = groupSet;
51
+ this.#patternSet = patterns;
52
+ this.#allowRepeats = options.allowRepeats;
53
+ }
54
+
55
+ get [Symbol.toStringTag]() {
56
+ return "Permute";
57
+ }
58
+
59
+ #describe() {
60
+ let description = "";
61
+
62
+ for (let pattern of this) {
63
+ description += util.inspect(pattern);
64
+ }
65
+
66
+ return description;
67
+ }
68
+
69
+ [util.inspect.custom](depth, opts) {
70
+ return `${this[Symbol.toStringTag]} {${this.#describe()}}`;
71
+ }
72
+
73
+ *[Symbol.iterator]() {
74
+ for (let group of this.groups) {
75
+ for (let pattern of this.patterns) {
76
+ let active = bitCount(group.group);
77
+
78
+ // Only do this pattern if it has enough chords to satisfy the group.
79
+ if (this.allowRepeats || group.length >= active) {
80
+ // Create each permutation of the current pattern for the current group.
81
+ for (let indices of permutationIndexGenerator(
82
+ active,
83
+ pattern.size,
84
+ this.allowRepeats
85
+ )) {
86
+ let resultPattern = new Pattern();
87
+
88
+ let groupBits = group.group;
89
+ let groupLength = group.length;
90
+ let index = 0;
91
+
92
+ while (groupLength-- > 0) {
93
+ // Add the selected chord if this position is active, otherwise add a rest chord.
94
+ resultPattern.add(groupBits & 1 ? pattern.elementAt(indices[index++]) : Chord.rest);
95
+ groupBits >>= 1;
96
+ }
97
+
98
+ yield resultPattern;
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+
105
+ get groups() {
106
+ return this.#groupSet;
107
+ }
108
+
109
+ get patterns() {
110
+ return this.#patternSet;
111
+ }
112
+
113
+ get allowRepeats() {
114
+ return this.#allowRepeats;
115
+ }
116
+
117
+ // Return the number of patterns generated by permuting the
118
+ // given group with the given pattern.
119
+ #groupSize(group, pattern) {
120
+ let active = bitCount(group.group);
121
+
122
+ return this.allowRepeats
123
+ ? pattern.size ** active
124
+ : permutations(pattern.size, active);
125
+ }
126
+
127
+ get size() {
128
+ let size = 0;
129
+
130
+ for (let group of this.groups) {
131
+ for (let pattern of this.patterns) {
132
+ size += this.#groupSize(group, pattern);
133
+ }
134
+ }
135
+
136
+ return size;
137
+ }
138
+
139
+ elementAt(index = 0) {
140
+ for (let group of this.groups) {
141
+ for (let pattern of this.patterns) {
142
+ let groupSize = this.#groupSize(group, pattern);
143
+ let patternSize = pattern.size;
144
+ let indices = [];
145
+ let active = bitCount(group.group);
146
+ let modulus = 0;
147
+
148
+ if (index < groupSize) {
149
+ if (this.#allowRepeats) {
150
+ while (active-- > 0) {
151
+ modulus = (patternSize ** active);
152
+ indices.push(Math.floor(index / modulus));
153
+ index %= modulus;
154
+ }
155
+ } else {
156
+ let unusedIndices = [...Array(patternSize).keys()];
157
+ let modulus = factorial(unusedIndices.length);
158
+ let localIndex = 0;
159
+
160
+ while (active-- > 0) {
161
+ modulus = Math.floor(modulus / unusedIndices.length);
162
+ localIndex = Math.floor(index / modulus);
163
+ indices.push(unusedIndices[localIndex]);
164
+ unusedIndices.splice(localIndex, 1);
165
+ index %= modulus;
166
+ }
167
+ }
168
+
169
+ let resultPattern = new Pattern();
170
+
171
+ let groupBits = group.group;
172
+ let groupLength = group.length;
173
+
174
+ index = 0;
175
+
176
+ while (groupLength-- > 0) {
177
+ // Add the selected chord if this position is active, otherwise add a rest chord.
178
+ resultPattern.add(groupBits & 1 ? pattern.elementAt(indices[index++]) : Chord.rest);
179
+ groupBits >>= 1;
180
+ }
181
+
182
+ return resultPattern;
183
+ }
184
+
185
+ index -= groupSize;
186
+ }
187
+ }
188
+
189
+ throw new Error("Invalid index");
190
+ }
191
+
192
+ #hasPattern(element, group, pattern) {
193
+ let groupLength = group.length;
194
+
195
+ if (element.size != groupLength) return false;
196
+
197
+ let groupBits = group.group;
198
+ let elementIndex = 0;
199
+ let patternIndex = -1;
200
+ let usedIndices = [];
201
+ let chord = undefined;
202
+
203
+ while (groupLength-- > 0) {
204
+ if (groupBits & 1) {
205
+ if (this.#allowRepeats) {
206
+ if (!pattern.has(element.elementAt(elementIndex))) return false;
207
+ } else {
208
+ chord = element.elementAt(elementIndex);
209
+
210
+ do {
211
+ patternIndex = pattern.indexOf(chord, patternIndex);
212
+ } while (usedIndices.includes(patternIndex));
213
+
214
+ if (patternIndex < 0 || usedIndices.includes(patternIndex)) return false;
215
+
216
+ usedIndices.push(patternIndex);
217
+ }
218
+ } else if (!group.elementAt(index).isEqual(Chord.rest)) {
219
+ return false;
220
+ }
221
+
222
+ ++elementIndex;
223
+ groupBits >>= 1;
224
+ }
225
+
226
+ return true;
227
+ }
228
+
229
+ has(element) {
230
+ for (let group of this.groups) {
231
+ for (let pattern of this.patterns) {
232
+ if (this.#hasPattern(element, group, pattern)) return true;
233
+ }
234
+ }
235
+
236
+ return false;
237
+ }
238
+ };
239
+
240
+ constructor(options = {}) {
241
+ super(options);
242
+ }
243
+
244
+ get allowedElements() {
245
+ return [Group];
246
+ }
247
+
248
+ get [Symbol.toStringTag]() {
249
+ return "GroupSet";
250
+ }
251
+
252
+ // Permute a Pattern, or each Pattern of a PatternSet, with each
253
+ // Group in this GroupSet to produce a new PatternSet.
254
+ //
255
+ // other must be a Pattern or PatternSet.
256
+ permute(patterns, allowRepeats = false) {
257
+ return new GroupSet.Permute(this, patterns, allowRepeats);
258
+ }
259
+ }
@@ -0,0 +1,105 @@
1
+ // instrument.mjs
2
+ //
3
+ // An instrument is a playing surface that generates a note.
4
+
5
+ import util from "util";
6
+
7
+ import { Element } from "./element.mjs";
8
+
9
+ export class Instrument extends Element {
10
+ #symbol = undefined; // Symbol representing the instrument.
11
+
12
+ constructor(id) {
13
+ super();
14
+
15
+ if (typeof id === 'string' || id instanceof String) {
16
+ this.#symbol = Symbol.for(id);
17
+ }
18
+ }
19
+
20
+ get [Symbol.toStringTag]() {
21
+ return "Instrument";
22
+ }
23
+
24
+ [util.inspect.custom](depth, opts) {
25
+ return `Instrument {${
26
+ !!this.#symbol ? this.#symbol.toString() : "undefined"
27
+ }}`;
28
+ }
29
+
30
+ get symbol() { return this.#symbol; }
31
+
32
+ isEqual(other) {
33
+ return super.isEqual(other) && this.symbol == other.symbol;
34
+ }
35
+ }
36
+
37
+ // Library of defined instruments.
38
+ export class Instruments {
39
+ static Bass1 = new Instrument("bass1");
40
+ static Bass1 = new Instrument("bass1");
41
+ static Bass2 = new Instrument("bass2");
42
+ static Casaba = new Instrument("casaba");
43
+ static China = new Instrument("china");
44
+ static Claves = new Instrument("claves");
45
+ static ClosedHighHat = new Instrument("closedhighhat");
46
+ static CowBell = new Instrument("cowbell");
47
+ static Crash = new Instrument("crash");
48
+ static CrashA = new Instrument("crasha");
49
+ static CrashB = new Instrument("crashb");
50
+ static Guiro = new Instrument("guiro");
51
+ static HalfOpenHighHat = new Instrument("halfopenhighhat");
52
+ static HandClap = new Instrument("handclap");
53
+ static HiBongo = new Instrument("hibongo");
54
+ static HiConga = new Instrument("hiconga");
55
+ static HighFloorTom = new Instrument("highfloortom");
56
+ static HighHat = new Instrument("highhat");
57
+ static HighHatPedal = new Instrument("highhatpedal");
58
+ static HighMidTom = new Instrument("highmidtom");
59
+ static HighTom = new Instrument("hightom");
60
+ static HighWoodBlock = new Instrument("highwoodblock");
61
+ static HiGogo = new Instrument("higogo");
62
+ static HiTimbale = new Instrument("hitimbale");
63
+ static LongGuiro = new Instrument("longguiro");
64
+ static LongWhistle = new Instrument("longwhistle");
65
+ static LowBongo = new Instrument("lowbongo");
66
+ static LowConga = new Instrument("lowconga");
67
+ static LowFloorTom = new Instrument("lowfloortom");
68
+ static LowGogo = new Instrument("lowgogo");
69
+ static LowMidTom = new Instrument("lowmidtom");
70
+ static LowTimbale = new Instrument("lowtimbale");
71
+ static LowTom = new Instrument("lowtom");
72
+ static LowWoodBlock = new Instrument("lowwoodblock");
73
+ static Maracas = new Instrument("maracas");
74
+ static MuteCuica = new Instrument("mutecuica");
75
+ static MuteHiBongo = new Instrument("mutehibongo");
76
+ static MuteHiConga = new Instrument("mutehiconga");
77
+ static MuteHiTimbale = new Instrument("mutehitimbale");
78
+ static MuteLowBongo = new Instrument("mutelowbongo");
79
+ static MuteLowConga = new Instrument("mutelowconga");
80
+ static MuteLowTimbale = new Instrument("mutelowtimbale");
81
+ static MuteTriangle = new Instrument("mutetriangle");
82
+ static OpenCuica = new Instrument("opencuica");
83
+ static OpenHiBongo = new Instrument("openhibongo");
84
+ static OpenHiConga = new Instrument("openhiconga");
85
+ static OpenHighHat = new Instrument("openhighhat");
86
+ static OpenHiTimbale = new Instrument("openhitimbale");
87
+ static OpenLowBongo = new Instrument("openlowbongo");
88
+ static OpenLowConga = new Instrument("openlowconga");
89
+ static OpenLowTimbale = new Instrument("openlowtimbale");
90
+ static OpenTriangle = new Instrument("opentriangle");
91
+ static Ride = new Instrument("ride");
92
+ static RideA = new Instrument("ridea");
93
+ static RideB = new Instrument("rideb");
94
+ static RideBell = new Instrument("ridebell");
95
+ static ShortGuiro = new Instrument("shortguiro");
96
+ static ShortWhistle = new Instrument("shortwhistle");
97
+ static SideStick = new Instrument("lowgogo");
98
+ static Snare = new Instrument("snare");
99
+ static Splash = new Instrument("splash");
100
+ static Tambourine = new Instrument("tambourine");
101
+ static TamTam = new Instrument("tamtam");
102
+ static Triangle = new Instrument("triangle");
103
+ static VibraSlap = new Instrument("vibraslap");
104
+ static WoodBlock = new Instrument("woodblock");
105
+ }
@@ -0,0 +1,20 @@
1
+ // InstrumentSet
2
+ //
3
+ // A set of instruments.
4
+
5
+ import { Set } from './set.mjs'
6
+ import { Instrument } from './instrument.mjs'
7
+
8
+ export class InstrumentSet extends Set {
9
+ constructor(options = {}) {
10
+ super(options)
11
+ }
12
+
13
+ get allowedElements() {
14
+ return [ Instrument ];
15
+ }
16
+
17
+ get [Symbol.toStringTag]() {
18
+ return "InstrumentSet";
19
+ }
20
+ }
@@ -0,0 +1,224 @@
1
+ // IrregularGroupGenerator is a Group generator that generates groups of lengths
2
+ // determined by a range (as with RegularGroupSet), except that the number of active
3
+ // chords in a group varies based on another range, and all permutation of such groups
4
+ // are generated.
5
+
6
+ import util from "util";
7
+
8
+ import { bitCount } from "./util.mjs";
9
+ import { combinations, ithBinaryCombination } from "./permute.mjs";
10
+
11
+ import { Group } from "./group.mjs";
12
+ import { RegularGroupSet } from "./regulargroupset.mjs";
13
+
14
+ export class IrregularGroupSet extends RegularGroupSet {
15
+ #min = 1;
16
+ #max = undefined;
17
+ #stepping = 1;
18
+ #reverseMax = false;
19
+
20
+ // Construct an IregularGroupSet with args:
21
+ // start The minimum number of chords in the generated groups. Default is 1.
22
+ // end The maximum number of chords in the generated groups. Must be >= start.
23
+ // If undefined then uses start. Default is undefined.
24
+ // step Step rate to increase group size by. Must be > 0. Default is 1.
25
+ //
26
+ // min The minimum number of active chords in a generated group. Must be >= 1. Default is 1.
27
+ // max The maximum number of active chords in a generated group. Must be >= min.
28
+ // If undefined then uses the group size and reverseMax is ignored. Default is undefined.
29
+ // stepping Step rate to increase number of active chords by. Must be > 0. Default is 1.
30
+ // reverseMax If true, max is calculated as MAX(min, group_size - max) instead of being taken literally.
31
+ // eg. if min=1, max=2 and the current group size is 5, then max = MAX(1, 5-2) = 3, and
32
+ // active chords will range in number from 1 to 3.
33
+ // Defauls it false.
34
+ constructor(options) {
35
+ super(options);
36
+
37
+ const defaults = { min: 1, max: undefined, stepping: 1, reverseMax: false };
38
+ options = { ...defaults, ...options };
39
+ let { min, max, stepping, reverseMax } = options;
40
+
41
+ if (min < 1)
42
+ throw new Error(
43
+ `Invalid minimum number of active chords - must be 1 or greater (${this.start})`
44
+ );
45
+ if (max !== undefined && max < min)
46
+ throw new Error(
47
+ `Invalid maximum number of chords - must be no less than min (${min})`
48
+ );
49
+ if (1 > stepping || stepping > Group.MAX_LENGTH)
50
+ throw new Error(
51
+ `Invalid step size - must be from 1 to ${Group.MAX_LENGTH - 1}`
52
+ );
53
+
54
+ this.#min = min;
55
+ this.#max = max;
56
+ this.#stepping = stepping;
57
+ this.#reverseMax = reverseMax;
58
+ }
59
+
60
+ // Overide Set.clone.
61
+ clone() {
62
+ let clone = new this.constructor({
63
+ start: this.start,
64
+ end: this.end,
65
+ step: this.step,
66
+ max: this.#max,
67
+ min: this.#min,
68
+ stepping: this.#stepping,
69
+ reverseMax: this.#reverseMax,
70
+ });
71
+
72
+ return clone;
73
+ }
74
+
75
+ get min() {
76
+ return this.#min;
77
+ }
78
+
79
+ get max() {
80
+ return this.#max;
81
+ }
82
+
83
+ get stepping() {
84
+ return this.#stepping;
85
+ }
86
+
87
+ get reverseMax() {
88
+ return this.#reverseMax;
89
+ }
90
+
91
+ get [Symbol.toStringTag]() {
92
+ return "IrregularGroupSet";
93
+ }
94
+
95
+ #actualMax(length) {
96
+ return Math.max(
97
+ this.min,
98
+ this.max === undefined
99
+ ? length
100
+ : this.reverseMax
101
+ ? length - this.max
102
+ : Math.min(length, this.max)
103
+ );
104
+ }
105
+
106
+ [util.inspect.custom](depth, opts) {
107
+ let groups = "";
108
+
109
+ for (const group of this) {
110
+ groups += (groups.length == 0 ? "" : ", ") + util.inspect(group);
111
+ }
112
+
113
+ return `${this[Symbol.toStringTag]} {[${groups}]}`;
114
+ }
115
+
116
+ *[Symbol.iterator]() {
117
+ // For each group size...
118
+ for (let length = this.start; length <= this.end; length += this.step) {
119
+ // Permutate the active number of chords...
120
+ const max = this.#actualMax(length);
121
+
122
+ for (let active = this.min; active <= max; active += this.stepping) {
123
+ // Calculate each permutation of <active> chords in a group size of <length>.
124
+ let index = 0;
125
+ let group = 0;
126
+
127
+ while ((group = ithBinaryCombination(length, active, index++))) {
128
+ yield new Group(group, length);
129
+ }
130
+ }
131
+ }
132
+ }
133
+
134
+ elementAt(index = 0) {
135
+ if (index >= 0 && index < this.size) {
136
+ for (let length = this.start; length <= this.end; length += this.step) {
137
+ const max = this.#actualMax(length);
138
+
139
+ for (let active = this.min; active <= max; active += this.stepping) {
140
+ const combos = combinations(length, active);
141
+
142
+ if (index < combos) {
143
+ return new Group(
144
+ ithBinaryCombination(length, active, index),
145
+ length
146
+ );
147
+ } else {
148
+ index -= combos;
149
+ }
150
+ }
151
+ }
152
+
153
+ return new Group(ithBinaryCombination(length, active, index++), length);
154
+ }
155
+
156
+ throw new Error("Invalid index");
157
+ }
158
+
159
+ get size() {
160
+ let size = 0;
161
+
162
+ for (let length = this.start; length <= this.end; length += this.step) {
163
+ // Permutate the active number of chords...
164
+ const max = this.#actualMax(length);
165
+
166
+ for (let active = this.min; active <= max; active += this.stepping) {
167
+ size += combinations(length, active);
168
+ }
169
+ }
170
+
171
+ return size;
172
+ }
173
+
174
+ // Returns whether the given element exists in this Set.
175
+ has(element) {
176
+ if (element === undefined || !(element instanceof Group)) {
177
+ throw new Error("Argument must be a Group");
178
+ }
179
+
180
+ const length = element.length;
181
+
182
+ // Fail if length is outside of bounds or not included by the step.
183
+ if (
184
+ length < this.start
185
+ || this.end < length
186
+ || ((length-1) % this.step) !== 0
187
+ ) {
188
+ return false;
189
+ }
190
+
191
+ let group = element.group;
192
+ let active = bitCount(group);
193
+ const max = this.#actualMax(length);
194
+
195
+ // Fail if active bits is outside of bounds or not included by the stepping.
196
+ if (
197
+ active < this.min
198
+ || max < active
199
+ || ((active-1) % this.stepping) !== 0
200
+ )
201
+ {
202
+ return false;
203
+ }
204
+
205
+ return true;
206
+ // let inactive = length - active;
207
+
208
+ // while (active + inactive > 0) {
209
+ // if (group & 1) {
210
+ // if (active-- === 0) {
211
+ // return false;
212
+ // }
213
+ // } else {
214
+ // if (inactive-- === 0) {
215
+ // return false;
216
+ // }
217
+ // }
218
+
219
+ // group >>= 1;
220
+ // }
221
+
222
+ // return group === 0;
223
+ }
224
+ }