scribbletune 5.2.0 → 5.4.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.
- package/dist/browser.cjs +1183 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.js +1135 -1
- package/dist/browser.js.map +1 -1
- package/dist/{index.mjs → index.cjs} +225 -169
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +323 -0
- package/dist/index.d.ts +303 -350
- package/dist/index.js +524 -1
- package/dist/index.js.map +1 -1
- package/dist/scribbletune.global.js +1944 -0
- package/dist/scribbletune.global.js.map +1 -0
- package/package.json +29 -40
- package/dist/lib/arp.d.ts +0 -10
- package/dist/lib/browser-clip.d.ts +0 -14
- package/dist/lib/browser-index.d.ts +0 -7
- package/dist/lib/channel.d.ts +0 -61
- package/dist/lib/clip.d.ts +0 -2
- package/dist/lib/index.d.ts +0 -7
- package/dist/lib/midi.d.ts +0 -11
- package/dist/lib/progression.d.ts +0 -25
- package/dist/lib/session.d.ts +0 -14
- package/dist/lib/types/arp-params.d.ts +0 -6
- package/dist/lib/types/channel-params.d.ts +0 -92
- package/dist/lib/types/channel-pattern.d.ts +0 -24
- package/dist/lib/types/clip-params.d.ts +0 -104
- package/dist/lib/types/event-fn.d.ts +0 -7
- package/dist/lib/types/index.d.ts +0 -14
- package/dist/lib/types/note-object.d.ts +0 -6
- package/dist/lib/types/nvp.d.ts +0 -4
- package/dist/lib/types/play-params.d.ts +0 -15
- package/dist/lib/types/player-observer-fn.d.ts +0 -6
- package/dist/lib/types/progression-scale.d.ts +0 -2
- package/dist/lib/types/seq-fn.d.ts +0 -2
- package/dist/lib/types/sizzle-style.d.ts +0 -2
- package/dist/lib/types/synth-params.d.ts +0 -20
- package/dist/lib/types/tpd.d.ts +0 -15
- package/dist/lib/utils.d.ts +0 -56
- package/dist/scribbletune.js +0 -2
- package/dist/scribbletune.js.map +0 -1
|
@@ -1,8 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
1
30
|
// src/index.ts
|
|
2
|
-
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
arp: () => arp,
|
|
34
|
+
chord: () => import_harmonics4.chord,
|
|
35
|
+
chords: () => import_harmonics4.chords,
|
|
36
|
+
clip: () => clip,
|
|
37
|
+
getChordDegrees: () => getChordDegrees,
|
|
38
|
+
getChordsByProgression: () => getChordsByProgression,
|
|
39
|
+
midi: () => midi,
|
|
40
|
+
mode: () => import_harmonics4.scale,
|
|
41
|
+
modes: () => import_harmonics4.scales,
|
|
42
|
+
progression: () => progression,
|
|
43
|
+
scale: () => import_harmonics4.scale,
|
|
44
|
+
scales: () => import_harmonics4.scales
|
|
45
|
+
});
|
|
46
|
+
module.exports = __toCommonJS(src_exports);
|
|
47
|
+
var import_harmonics4 = require("harmonics");
|
|
48
|
+
|
|
49
|
+
// src/arp.ts
|
|
50
|
+
var import_harmonics2 = require("harmonics");
|
|
3
51
|
|
|
4
52
|
// src/utils.ts
|
|
5
|
-
|
|
53
|
+
var import_harmonics = require("harmonics");
|
|
6
54
|
var isNote = (str) => /^[a-gA-G](?:#|b)?\d$/.test(str);
|
|
7
55
|
var expandStr = (str) => {
|
|
8
56
|
str = JSON.stringify(str.split(""));
|
|
@@ -32,10 +80,10 @@ var shuffle = (arr, fullShuffle = true) => {
|
|
|
32
80
|
});
|
|
33
81
|
return arr;
|
|
34
82
|
};
|
|
35
|
-
var pickOne = (arr) => arr
|
|
83
|
+
var pickOne = (arr) => arr[Math.floor(Math.random() * arr.length)];
|
|
36
84
|
var dice = () => !!Math.round(Math.random());
|
|
37
85
|
var errorHasMessage = (x) => {
|
|
38
|
-
return typeof x.message === "string";
|
|
86
|
+
return typeof x === "object" && x !== null && "message" in x && typeof x.message === "string";
|
|
39
87
|
};
|
|
40
88
|
var convertChordToNotes = (el) => {
|
|
41
89
|
let c1;
|
|
@@ -43,12 +91,12 @@ var convertChordToNotes = (el) => {
|
|
|
43
91
|
let e1;
|
|
44
92
|
let e2;
|
|
45
93
|
try {
|
|
46
|
-
c1 = inlineChord(el);
|
|
94
|
+
c1 = (0, import_harmonics.inlineChord)(el);
|
|
47
95
|
} catch (e) {
|
|
48
96
|
e1 = e;
|
|
49
97
|
}
|
|
50
98
|
try {
|
|
51
|
-
c2 = chord(el.replace(/_/g, " "));
|
|
99
|
+
c2 = (0, import_harmonics.chord)(el.replace(/_/g, " "));
|
|
52
100
|
} catch (e) {
|
|
53
101
|
e2 = e;
|
|
54
102
|
}
|
|
@@ -64,7 +112,7 @@ var convertChordToNotes = (el) => {
|
|
|
64
112
|
if (!e2) {
|
|
65
113
|
return c2;
|
|
66
114
|
}
|
|
67
|
-
return chord(el);
|
|
115
|
+
return (0, import_harmonics.chord)(el);
|
|
68
116
|
};
|
|
69
117
|
var convertChordsToNotes = (el) => {
|
|
70
118
|
if (typeof el === "string" && isNote(el)) {
|
|
@@ -86,7 +134,7 @@ var convertChordsToNotes = (el) => {
|
|
|
86
134
|
}
|
|
87
135
|
if (!Array.isArray(el)) {
|
|
88
136
|
const c = convertChordToNotes(el);
|
|
89
|
-
if (c
|
|
137
|
+
if (c?.length) {
|
|
90
138
|
return c;
|
|
91
139
|
}
|
|
92
140
|
}
|
|
@@ -94,8 +142,79 @@ var convertChordsToNotes = (el) => {
|
|
|
94
142
|
};
|
|
95
143
|
var randomInt = (num = 1) => Math.round(Math.random() * num);
|
|
96
144
|
|
|
97
|
-
// src/
|
|
98
|
-
var
|
|
145
|
+
// src/arp.ts
|
|
146
|
+
var DEFAULT_OCTAVE = 4;
|
|
147
|
+
var fillArr = (arr, len) => {
|
|
148
|
+
const bumpOctave = (el) => {
|
|
149
|
+
if (!el) {
|
|
150
|
+
throw new Error("Empty element");
|
|
151
|
+
}
|
|
152
|
+
const note = el.replace(/\d/, "");
|
|
153
|
+
const oct = el.replace(/\D/g, "") || DEFAULT_OCTAVE;
|
|
154
|
+
if (!note) {
|
|
155
|
+
throw new Error("Incorrect note");
|
|
156
|
+
}
|
|
157
|
+
return note + (+oct + 1);
|
|
158
|
+
};
|
|
159
|
+
const arr1 = arr.map(bumpOctave);
|
|
160
|
+
const arr2 = arr1.map(bumpOctave);
|
|
161
|
+
const finalArr = [...arr, ...arr1, ...arr2];
|
|
162
|
+
return finalArr.slice(0, len);
|
|
163
|
+
};
|
|
164
|
+
var arp = (chordsOrParams) => {
|
|
165
|
+
let finalArr = [];
|
|
166
|
+
const params = {
|
|
167
|
+
count: 4,
|
|
168
|
+
order: "0123",
|
|
169
|
+
chords: ""
|
|
170
|
+
};
|
|
171
|
+
if (typeof chordsOrParams === "string") {
|
|
172
|
+
params.chords = chordsOrParams;
|
|
173
|
+
} else {
|
|
174
|
+
if (chordsOrParams.order?.match(/\D/g)) {
|
|
175
|
+
throw new TypeError("Invalid value for order");
|
|
176
|
+
}
|
|
177
|
+
if (chordsOrParams.count > 8 || chordsOrParams.count < 2) {
|
|
178
|
+
throw new TypeError("Invalid value for count");
|
|
179
|
+
}
|
|
180
|
+
if (chordsOrParams.count && !chordsOrParams.order) {
|
|
181
|
+
params.order = Array.from(Array(chordsOrParams.count).keys()).join("");
|
|
182
|
+
}
|
|
183
|
+
Object.assign(params, chordsOrParams);
|
|
184
|
+
}
|
|
185
|
+
if (typeof params.chords === "string") {
|
|
186
|
+
const chordsArr = params.chords.split(" ");
|
|
187
|
+
chordsArr.forEach((c, i) => {
|
|
188
|
+
try {
|
|
189
|
+
const filledArr = fillArr((0, import_harmonics2.inlineChord)(c), params.count);
|
|
190
|
+
const reorderedArr = params.order.split("").map((idx) => filledArr[Number(idx)]);
|
|
191
|
+
finalArr = [...finalArr, ...reorderedArr];
|
|
192
|
+
} catch (_e) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
`Cannot decode chord ${i + 1} "${c}" in given "${params.chords}"`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
} else if (Array.isArray(params.chords)) {
|
|
199
|
+
params.chords.forEach((c, i) => {
|
|
200
|
+
try {
|
|
201
|
+
const filledArr = fillArr(c, params.count);
|
|
202
|
+
const reorderedArr = params.order.split("").map((idx) => filledArr[Number(idx)]);
|
|
203
|
+
finalArr = [...finalArr, ...reorderedArr];
|
|
204
|
+
} catch (e) {
|
|
205
|
+
throw new Error(
|
|
206
|
+
`${errorHasMessage(e) ? e.message : e} in chord ${i + 1} "${c}"`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
} else {
|
|
211
|
+
throw new TypeError("Invalid value for chords");
|
|
212
|
+
}
|
|
213
|
+
return finalArr;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// src/clip-utils.ts
|
|
217
|
+
var defaultParams = {
|
|
99
218
|
notes: ["C4"],
|
|
100
219
|
pattern: "x",
|
|
101
220
|
shuffle: false,
|
|
@@ -107,30 +226,21 @@ var getDefaultParams = () => ({
|
|
|
107
226
|
accentLow: 70,
|
|
108
227
|
randomNotes: null,
|
|
109
228
|
offlineRendering: false
|
|
110
|
-
});
|
|
111
|
-
var hdr = {
|
|
112
|
-
"1m": 2048,
|
|
113
|
-
"2m": 4096,
|
|
114
|
-
"3m": 6144,
|
|
115
|
-
"4m": 8192,
|
|
116
|
-
"1n": 512,
|
|
117
|
-
"2n": 256,
|
|
118
|
-
"4n": 128,
|
|
119
|
-
"8n": 64,
|
|
120
|
-
"16n": 32
|
|
121
229
|
};
|
|
122
|
-
var
|
|
123
|
-
|
|
124
|
-
if (typeof params.notes === "string") {
|
|
125
|
-
params.notes = params.notes.replace(/\s{2,}/g, " ");
|
|
126
|
-
params.notes = params.notes.split(" ");
|
|
127
|
-
}
|
|
128
|
-
params.notes = params.notes ? params.notes.map(convertChordsToNotes) : [];
|
|
129
|
-
if (/[^x\-_[\]R]/.test(params.pattern)) {
|
|
230
|
+
var validatePattern = (pattern) => {
|
|
231
|
+
if (/[^x\-_[\]R]/.test(pattern)) {
|
|
130
232
|
throw new TypeError(
|
|
131
|
-
`pattern can only comprise x - _ [ ] R, found ${
|
|
233
|
+
`pattern can only comprise x - _ [ ] R, found ${pattern}`
|
|
132
234
|
);
|
|
133
235
|
}
|
|
236
|
+
};
|
|
237
|
+
var preprocessClipParams = (params, extraDefaults) => {
|
|
238
|
+
params = { ...defaultParams, ...extraDefaults, ...params || {} };
|
|
239
|
+
if (typeof params.notes === "string") {
|
|
240
|
+
params.notes = params.notes.replace(/\s{2,}/g, " ").split(" ");
|
|
241
|
+
}
|
|
242
|
+
params.notes = params.notes ? params.notes.map(convertChordsToNotes) : [];
|
|
243
|
+
validatePattern(params.pattern);
|
|
134
244
|
if (params.shuffle) {
|
|
135
245
|
params.notes = shuffle(params.notes);
|
|
136
246
|
}
|
|
@@ -142,12 +252,28 @@ var clip = (params) => {
|
|
|
142
252
|
convertChordsToNotes
|
|
143
253
|
);
|
|
144
254
|
}
|
|
255
|
+
return params;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// src/clip.ts
|
|
259
|
+
var hdr = {
|
|
260
|
+
"1m": 2048,
|
|
261
|
+
"2m": 4096,
|
|
262
|
+
"3m": 6144,
|
|
263
|
+
"4m": 8192,
|
|
264
|
+
"1n": 512,
|
|
265
|
+
"2n": 256,
|
|
266
|
+
"4n": 128,
|
|
267
|
+
"8n": 64,
|
|
268
|
+
"16n": 32
|
|
269
|
+
};
|
|
270
|
+
var clip = (params) => {
|
|
271
|
+
params = preprocessClipParams(params);
|
|
145
272
|
const clipNotes = [];
|
|
146
273
|
let step = 0;
|
|
147
274
|
const recursivelyApplyPatternToNotes = (patternArr, length, parentNoteLength) => {
|
|
148
275
|
let totalLength = 0;
|
|
149
276
|
patternArr.forEach((char, idx) => {
|
|
150
|
-
var _a;
|
|
151
277
|
if (typeof char === "string") {
|
|
152
278
|
let note = null;
|
|
153
279
|
if (char === "-") {
|
|
@@ -183,7 +309,7 @@ var clip = (params) => {
|
|
|
183
309
|
lastClipNote.length = lastClipNote.length + diff;
|
|
184
310
|
}
|
|
185
311
|
}
|
|
186
|
-
if (step ===
|
|
312
|
+
if (step === params.notes?.length) {
|
|
187
313
|
step = 0;
|
|
188
314
|
}
|
|
189
315
|
}
|
|
@@ -265,8 +391,62 @@ var clip = (params) => {
|
|
|
265
391
|
return clipNotes;
|
|
266
392
|
};
|
|
267
393
|
|
|
394
|
+
// src/midi.ts
|
|
395
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
396
|
+
var import_midi = require("@scribbletune/midi");
|
|
397
|
+
var midi = (notes, fileName = "music.mid", bpm) => {
|
|
398
|
+
const file = createFileFromNotes(notes, bpm);
|
|
399
|
+
const bytes = file.toBytes();
|
|
400
|
+
if (fileName === null) {
|
|
401
|
+
return bytes;
|
|
402
|
+
}
|
|
403
|
+
if (!fileName.endsWith(".mid")) {
|
|
404
|
+
fileName = `${fileName}.mid`;
|
|
405
|
+
}
|
|
406
|
+
if (typeof window !== "undefined" && window.URL && typeof window.URL.createObjectURL === "function") {
|
|
407
|
+
return createDownloadLink(bytes, fileName);
|
|
408
|
+
}
|
|
409
|
+
import_node_fs.default.writeFileSync(fileName, bytes, "binary");
|
|
410
|
+
console.log(`MIDI file generated: ${fileName}.`);
|
|
411
|
+
};
|
|
412
|
+
var createDownloadLink = (b, fileName) => {
|
|
413
|
+
const bytes = new Uint8Array(b.length);
|
|
414
|
+
for (let i = 0; i < b.length; i++) {
|
|
415
|
+
const ascii = b.charCodeAt(i);
|
|
416
|
+
bytes[i] = ascii;
|
|
417
|
+
}
|
|
418
|
+
const blob = new Blob([bytes], { type: "audio/midi" });
|
|
419
|
+
const link = document.createElement("a");
|
|
420
|
+
link.href = typeof window !== "undefined" && typeof window.URL !== "undefined" && typeof window.URL.createObjectURL !== "undefined" && window.URL.createObjectURL(blob) || "";
|
|
421
|
+
link.download = fileName;
|
|
422
|
+
link.innerText = "Download MIDI file";
|
|
423
|
+
return link;
|
|
424
|
+
};
|
|
425
|
+
var createFileFromNotes = (notes, bpm) => {
|
|
426
|
+
const file = new import_midi.File();
|
|
427
|
+
const track = new import_midi.Track();
|
|
428
|
+
if (typeof bpm === "number") {
|
|
429
|
+
track.setTempo(bpm);
|
|
430
|
+
}
|
|
431
|
+
file.addTrack(track);
|
|
432
|
+
for (const noteObj of notes) {
|
|
433
|
+
const level = noteObj.level || 127;
|
|
434
|
+
if (noteObj.note) {
|
|
435
|
+
if (typeof noteObj.note === "string") {
|
|
436
|
+
track.noteOn(0, noteObj.note, noteObj.length, level);
|
|
437
|
+
track.noteOff(0, noteObj.note, noteObj.length, level);
|
|
438
|
+
} else {
|
|
439
|
+
track.addChord(0, noteObj.note, noteObj.length, level);
|
|
440
|
+
}
|
|
441
|
+
} else {
|
|
442
|
+
track.noteOff(0, "", noteObj.length);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return file;
|
|
446
|
+
};
|
|
447
|
+
|
|
268
448
|
// src/progression.ts
|
|
269
|
-
|
|
449
|
+
var import_harmonics3 = require("harmonics");
|
|
270
450
|
var getChordDegrees = (mode) => {
|
|
271
451
|
const theRomans = {
|
|
272
452
|
ionian: ["I", "ii", "iii", "IV", "V", "vi", "vii\xB0"],
|
|
@@ -299,10 +479,10 @@ var getChordName = (roman) => {
|
|
|
299
479
|
prefix = "m";
|
|
300
480
|
}
|
|
301
481
|
if (roman.indexOf("\xB0") > -1) {
|
|
302
|
-
return prefix
|
|
482
|
+
return `${prefix}7b5`;
|
|
303
483
|
}
|
|
304
484
|
if (roman.indexOf("+") > -1) {
|
|
305
|
-
return prefix
|
|
485
|
+
return `${prefix}#5`;
|
|
306
486
|
}
|
|
307
487
|
if (roman.indexOf("7") > -1) {
|
|
308
488
|
return prefix === "M" ? "maj7" : "m7";
|
|
@@ -315,14 +495,14 @@ var getChordsByProgression = (noteOctaveScale, chordDegress) => {
|
|
|
315
495
|
noteOctaveScaleArr[0] += "4";
|
|
316
496
|
noteOctaveScale = noteOctaveScaleArr.join(" ");
|
|
317
497
|
}
|
|
318
|
-
const mode = scale(noteOctaveScale);
|
|
498
|
+
const mode = (0, import_harmonics3.scale)(noteOctaveScale);
|
|
319
499
|
const chordDegreesArr = chordDegress.replace(/\s*,+\s*/g, " ").split(" ");
|
|
320
500
|
const chordFamily = chordDegreesArr.map((roman) => {
|
|
321
501
|
const chordName = getChordName(roman);
|
|
322
502
|
const scaleId = idxByDegree[roman.replace(/\W|\d/g, "").toLowerCase()];
|
|
323
503
|
const note = mode[scaleId];
|
|
324
504
|
const oct = note.replace(/\D+/, "");
|
|
325
|
-
return note.replace(/\d/, "") + chordName
|
|
505
|
+
return `${note.replace(/\d/, "") + chordName}_${oct}`;
|
|
326
506
|
});
|
|
327
507
|
return chordFamily.toString().replace(/,/g, " ");
|
|
328
508
|
};
|
|
@@ -373,143 +553,19 @@ var progression = (scaleType, count = 4) => {
|
|
|
373
553
|
}
|
|
374
554
|
return [];
|
|
375
555
|
};
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
import { inlineChord as inlineChord2 } from "harmonics";
|
|
379
|
-
var DEFAULT_OCTAVE = 4;
|
|
380
|
-
var fillArr = (arr, len) => {
|
|
381
|
-
const bumpOctave = (el) => {
|
|
382
|
-
if (!el) {
|
|
383
|
-
throw new Error("Empty element");
|
|
384
|
-
}
|
|
385
|
-
const note = el.replace(/\d/, "");
|
|
386
|
-
const oct = el.replace(/\D/g, "") || DEFAULT_OCTAVE;
|
|
387
|
-
if (!note) {
|
|
388
|
-
throw new Error("Incorrect note");
|
|
389
|
-
}
|
|
390
|
-
return note + (+oct + 1);
|
|
391
|
-
};
|
|
392
|
-
const arr1 = arr.map(bumpOctave);
|
|
393
|
-
const arr2 = arr1.map(bumpOctave);
|
|
394
|
-
const finalArr = [...arr, ...arr1, ...arr2];
|
|
395
|
-
return finalArr.slice(0, len);
|
|
396
|
-
};
|
|
397
|
-
var arp = (chordsOrParams) => {
|
|
398
|
-
let finalArr = [];
|
|
399
|
-
const params = {
|
|
400
|
-
count: 4,
|
|
401
|
-
order: "0123",
|
|
402
|
-
chords: ""
|
|
403
|
-
};
|
|
404
|
-
if (typeof chordsOrParams === "string") {
|
|
405
|
-
params.chords = chordsOrParams;
|
|
406
|
-
} else {
|
|
407
|
-
if (chordsOrParams.order && chordsOrParams.order.match(/\D/g)) {
|
|
408
|
-
throw new TypeError("Invalid value for order");
|
|
409
|
-
}
|
|
410
|
-
if (chordsOrParams.count > 8 || chordsOrParams.count < 2) {
|
|
411
|
-
throw new TypeError("Invalid value for count");
|
|
412
|
-
}
|
|
413
|
-
if (chordsOrParams.count && !chordsOrParams.order) {
|
|
414
|
-
params.order = Array.from(Array(chordsOrParams.count).keys()).join("");
|
|
415
|
-
}
|
|
416
|
-
Object.assign(params, chordsOrParams);
|
|
417
|
-
}
|
|
418
|
-
if (typeof params.chords === "string") {
|
|
419
|
-
const chordsArr = params.chords.split(" ");
|
|
420
|
-
chordsArr.forEach((c, i) => {
|
|
421
|
-
try {
|
|
422
|
-
const filledArr = fillArr(inlineChord2(c), params.count);
|
|
423
|
-
const reorderedArr = params.order.split("").map((idx) => filledArr[idx]);
|
|
424
|
-
finalArr = [...finalArr, ...reorderedArr];
|
|
425
|
-
} catch (e) {
|
|
426
|
-
throw new Error(
|
|
427
|
-
`Cannot decode chord ${i + 1} "${c}" in given "${params.chords}"`
|
|
428
|
-
);
|
|
429
|
-
}
|
|
430
|
-
});
|
|
431
|
-
} else if (Array.isArray(params.chords)) {
|
|
432
|
-
params.chords.forEach((c, i) => {
|
|
433
|
-
try {
|
|
434
|
-
const filledArr = fillArr(c, params.count);
|
|
435
|
-
const reorderedArr = params.order.split("").map((idx) => filledArr[idx]);
|
|
436
|
-
finalArr = [...finalArr, ...reorderedArr];
|
|
437
|
-
} catch (e) {
|
|
438
|
-
throw new Error(
|
|
439
|
-
`${errorHasMessage(e) ? e.message : e} in chord ${i + 1} "${c}"`
|
|
440
|
-
);
|
|
441
|
-
}
|
|
442
|
-
});
|
|
443
|
-
} else {
|
|
444
|
-
throw new TypeError("Invalid value for chords");
|
|
445
|
-
}
|
|
446
|
-
return finalArr;
|
|
447
|
-
};
|
|
448
|
-
|
|
449
|
-
// src/midi.ts
|
|
450
|
-
import fs from "fs";
|
|
451
|
-
import * as jsmidgen from "jsmidgen";
|
|
452
|
-
var midi = (notes, fileName = "music.mid", bpm) => {
|
|
453
|
-
const file = createFileFromNotes(notes, bpm);
|
|
454
|
-
const bytes = file.toBytes();
|
|
455
|
-
if (fileName === null) {
|
|
456
|
-
return bytes;
|
|
457
|
-
}
|
|
458
|
-
if (!fileName.endsWith(".mid")) {
|
|
459
|
-
fileName = fileName + ".mid";
|
|
460
|
-
}
|
|
461
|
-
if (typeof window !== "undefined" && window.URL && window.URL.createObjectURL) {
|
|
462
|
-
return createDownloadLink(bytes, fileName);
|
|
463
|
-
}
|
|
464
|
-
fs.writeFileSync(fileName, bytes, "binary");
|
|
465
|
-
console.log(`MIDI file generated: ${fileName}.`);
|
|
466
|
-
};
|
|
467
|
-
var createDownloadLink = (b, fileName) => {
|
|
468
|
-
const bytes = new Uint8Array(b.length);
|
|
469
|
-
for (let i = 0; i < b.length; i++) {
|
|
470
|
-
const ascii = b.charCodeAt(i);
|
|
471
|
-
bytes[i] = ascii;
|
|
472
|
-
}
|
|
473
|
-
const blob = new Blob([bytes], { type: "audio/midi" });
|
|
474
|
-
const link = document.createElement("a");
|
|
475
|
-
link.href = typeof window !== "undefined" && typeof window.URL !== "undefined" && typeof window.URL.createObjectURL !== "undefined" && window.URL.createObjectURL(blob) || "";
|
|
476
|
-
link.download = fileName;
|
|
477
|
-
link.innerText = "Download MIDI file";
|
|
478
|
-
return link;
|
|
479
|
-
};
|
|
480
|
-
var createFileFromNotes = (notes, bpm) => {
|
|
481
|
-
const file = new jsmidgen.File();
|
|
482
|
-
const track = new jsmidgen.Track();
|
|
483
|
-
if (typeof bpm === "number") {
|
|
484
|
-
track.setTempo(bpm);
|
|
485
|
-
}
|
|
486
|
-
file.addTrack(track);
|
|
487
|
-
for (const noteObj of notes) {
|
|
488
|
-
const level = noteObj.level || 127;
|
|
489
|
-
if (noteObj.note) {
|
|
490
|
-
if (typeof noteObj.note === "string") {
|
|
491
|
-
track.noteOn(0, noteObj.note, noteObj.length, level);
|
|
492
|
-
track.noteOff(0, noteObj.note, noteObj.length, level);
|
|
493
|
-
} else {
|
|
494
|
-
track.addChord(0, noteObj.note, noteObj.length, level);
|
|
495
|
-
}
|
|
496
|
-
} else {
|
|
497
|
-
track.noteOff(0, "", noteObj.length);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
return file;
|
|
501
|
-
};
|
|
502
|
-
export {
|
|
556
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
557
|
+
0 && (module.exports = {
|
|
503
558
|
arp,
|
|
504
|
-
|
|
559
|
+
chord,
|
|
505
560
|
chords,
|
|
506
561
|
clip,
|
|
507
562
|
getChordDegrees,
|
|
508
563
|
getChordsByProgression,
|
|
509
564
|
midi,
|
|
510
|
-
|
|
511
|
-
|
|
565
|
+
mode,
|
|
566
|
+
modes,
|
|
512
567
|
progression,
|
|
513
|
-
|
|
568
|
+
scale,
|
|
514
569
|
scales
|
|
515
|
-
};
|
|
570
|
+
});
|
|
571
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/arp.ts","../src/utils.ts","../src/clip-utils.ts","../src/clip.ts","../src/midi.ts","../src/progression.ts"],"sourcesContent":["import { chord, chords, scale, scales } from 'harmonics';\nimport { arp } from './arp';\nimport { clip } from './clip';\nimport { midi } from './midi';\nimport {\n getChordDegrees,\n getChordsByProgression,\n progression,\n} from './progression';\n\nexport * from './types';\nexport {\n scale,\n scale as mode,\n scales,\n scales as modes,\n chord,\n chords,\n clip,\n getChordDegrees,\n getChordsByProgression,\n progression,\n arp,\n midi,\n};\n","import { inlineChord } from 'harmonics';\nimport type { ArpParams } from './types';\nimport { errorHasMessage } from './utils';\n\nconst DEFAULT_OCTAVE = 4;\n\n/**\n * Take an array and fill it with it s own elements in the next octave till it s of the specified `len`\n * @param {Array} arr e.g. ['a4', 'b4']\n * @param {Number} e.g. len 4\n * @return {Array} e.g. ['a4', 'b4', 'a5', 'b5']\n */\nconst fillArr = (arr: string[], len: number): string[] => {\n const bumpOctave = (el: string): string => {\n if (!el) {\n throw new Error('Empty element');\n }\n const note = el.replace(/\\d/, '');\n const oct = el.replace(/\\D/g, '') || DEFAULT_OCTAVE;\n if (!note) {\n throw new Error('Incorrect note');\n }\n return note + (+oct + 1);\n };\n\n // Create a couple of chord arrays with bumped octaves\n // so that something like [c3, e3, g4] turns into [c4, e4, g5] and [c5, e5, g6]\n const arr1 = arr.map(bumpOctave);\n const arr2 = arr1.map(bumpOctave);\n const finalArr = [...arr, ...arr1, ...arr2];\n\n // Slice and return only as much as required\n return finalArr.slice(0, len);\n};\n\n/**\n *\n * @param chordsOrParams a string that denotes space (comma?) separated chords to be used or an object with additional properties\n * By default, if this is a string, the count of notes generated is 8 and the order is ascending.\n * For instance arp('CM FM') will result in an array of notes [C4, E4, G4, F4, A4, C4, C5, E5]\n * You can even provide Params as an object.\n * For e.g. arp({count: 8, order: '10325476', chords: 'FM_4 Gm7b5_4 AbM_4 Bbm_4 Cm_5 DbM_5 EbM_5})\n */\nexport const arp = (chordsOrParams: string | ArpParams): string[] => {\n let finalArr: string[] = [];\n const params: ArpParams = {\n count: 4,\n order: '0123',\n chords: '',\n };\n\n if (typeof chordsOrParams === 'string') {\n params.chords = chordsOrParams;\n } else {\n if (chordsOrParams.order?.match(/\\D/g)) {\n throw new TypeError('Invalid value for order');\n }\n\n if (chordsOrParams.count > 8 || chordsOrParams.count < 2) {\n throw new TypeError('Invalid value for count');\n }\n\n // Provision a order for the notes in case only count was provided\n if (chordsOrParams.count && !chordsOrParams.order) {\n params.order = Array.from(Array(chordsOrParams.count).keys()).join('');\n }\n Object.assign(params, chordsOrParams);\n }\n\n // Chords can be passed as a string, e.g. 'CM_4 FM_4'\n // or as an array of notes arrays e.g. [['C3', 'E3', 'G3', 'B3'], ['F3', 'A3', 'C4', 'E4']]\n if (typeof params.chords === 'string') {\n const chordsArr: string[] = params.chords.split(' ');\n chordsArr.forEach((c, i) => {\n try {\n const filledArr = fillArr(inlineChord(c), params.count);\n // reorder the filledArr as per params.order\n const reorderedArr = (params.order as string)\n .split('')\n .map((idx: string) => filledArr[Number(idx)]);\n finalArr = [...finalArr, ...reorderedArr];\n } catch (_e) {\n throw new Error(\n `Cannot decode chord ${i + 1} \"${c}\" in given \"${params.chords}\"`\n );\n }\n });\n } else if (Array.isArray(params.chords)) {\n params.chords.forEach((c, i) => {\n try {\n const filledArr = fillArr(c as string[], params.count);\n // reorder the filledArr as per params.order\n const reorderedArr = (params.order as string)\n .split('')\n .map((idx: string) => filledArr[Number(idx)]);\n finalArr = [...finalArr, ...reorderedArr];\n } catch (e) {\n throw new Error(\n `${errorHasMessage(e) ? e.message : e} in chord ${i + 1} \"${c}\"`\n );\n }\n });\n } else {\n throw new TypeError('Invalid value for chords');\n }\n\n return finalArr;\n};\n","import { chord, inlineChord } from 'harmonics';\nimport type { PatternElement } from './types';\n\n/**\n * Take a string input and check if it s a note name or not\n * @param {String} str Note name e.g. c4\n * @return {Boolean} Return true for c4 or return false for something like CM\n */\nexport const isNote = (str: string): boolean =>\n /^[a-gA-G](?:#|b)?\\d$/.test(str);\n\n/**\n * Take a String input such as xxx[xx[xx]]\n * and return an Array as ['x', 'x', 'x', ['x', 'x', ['x', 'x']]]\n * @param {String} str\n * @return {Array}\n */\nexport const expandStr = (str: string): PatternElement[] => {\n str = JSON.stringify(str.split(''));\n str = str.replace(/,\"\\[\",/g, ', [');\n str = str.replace(/\"\\[\",/g, '[');\n str = str.replace(/,\"\\]\"/g, ']');\n return JSON.parse(str);\n};\n\n/**\n * Basic Array randomizer\n * @param {Array} arr\n * @param {boolean} fullShuffle Ensure no elements remain in old place\n * @return {Array}\n */\nexport const shuffle = <T>(arr: T[], fullShuffle = true): T[] => {\n const lastIndex: number = arr.length - 1;\n\n // Shuffle algorithm by Richard Durstenfeld (Donald E. Knuth), also Ronald Fisher and Frank Yates.\n // \"Full Shuffle\" Modification to ensure no elements remain in their original place (by taking each element once\n // and swapping with any remaining elements)\n arr.forEach((el, idx: number) => {\n if (idx >= lastIndex) {\n // No shuffling last element\n // One before last is always swapped with last at the end of the loop\n // Since previous swaps can move last element into other places, there is still a random shuffle of last element\n return;\n }\n // Swap el with one of the higher elements randomly\n const rnd = fullShuffle\n ? // Pick random number from idx+1 to lastIndex (Modified algorithm, (N-1)! combinations)\n // Math.random -> [0, 1) -> [0, lastIndex-idx ) --floor-> [0, lastIndex-idx-1]\n // rnd = [0, lastIndex-idx-1] + 1 + idx = [1 + idx, lastIndex]\n // (Original algorithm would pick rnd = [idx, lastIndex], thus any element could arrive back into its slot)\n Math.floor(Math.random() * (lastIndex - idx)) + 1 + idx\n : // Pick random number from idx to lastIndex (Unmodified Richard Durstenfeld, N! combinations)\n Math.floor(Math.random() * (lastIndex + 1 - idx)) + idx;\n arr[idx] = arr[rnd];\n arr[rnd] = el;\n });\n\n return arr;\n};\n\n/**\n * Pick one item randomly from an array and return it\n * @param arr\n */\nexport const pickOne = <T = unknown>(arr: T[]): T =>\n arr[Math.floor(Math.random() * arr.length)];\n\n/**\n * Boolean generator\n */\nexport const dice = (): boolean => !!Math.round(Math.random());\n\n/** Type guard: check if a caught value has a string `message` property. */\nexport const errorHasMessage = (x: unknown): x is { message: string } => {\n return (\n typeof x === 'object' &&\n x !== null &&\n 'message' in x &&\n typeof (x as { message: unknown }).message === 'string'\n );\n};\n\n/**\n * 'el' could be an inlineChord() e.g. Cmaj7 or Dbsus2_5\n * or a chord() e.g. 'C3 M'\n */\nexport const convertChordToNotes = (el: string): string[] => {\n // Try both inlineChord() and chord()\n let c1: string[] | undefined;\n let c2: string[] | undefined;\n let e1: unknown;\n let e2: unknown;\n try {\n c1 = inlineChord(el);\n } catch (e) {\n e1 = e;\n }\n try {\n c2 = chord(el.replace(/_/g, ' ')); // chord() is not friendly to underscores\n } catch (e) {\n e2 = e;\n }\n\n if (!e1 && !e2) {\n // Both inlineChord() and chord() have result\n if (c1!.toString() !== c2!.toString()) {\n throw new Error(`Chord ${el} cannot decode, guessing ${c1} or ${c2}`);\n }\n return c1!;\n } // else\n if (!e1) {\n return c1!;\n } // else\n if (!e2) {\n return c2!;\n } // else\n\n // Give up, last try:\n return chord(el);\n};\n\n/**\n * Normalize a note, chord name, or array of notes into a string array.\n * Single notes become `['C4']`, chord names are expanded via harmonics.\n */\nexport const convertChordsToNotes = (\n el: string | (string | string[])[]\n): string[] => {\n if (typeof el === 'string' && isNote(el as string)) {\n // A note needs to be an array so that it can accomodate chords or single notes with a single interface\n return [el];\n }\n\n if (Array.isArray(el)) {\n // This could be a chord provided as an array or an array of arrays\n el.forEach(n => {\n // This could be a chord provided as an array\n if (Array.isArray(n)) {\n // TODO: Can we convert it to something useful?\n // make sure it uses valid notes\n n.forEach(n1 => {\n if (typeof n1 !== 'string' || !isNote(n1)) {\n throw new TypeError('array of arrays must comprise valid notes');\n }\n });\n // throw new TypeError('cannot decode array of arrays');\n } else if (typeof n !== 'string' || !isNote(n)) {\n // make sure it uses valid notes\n throw new TypeError('array must comprise valid notes');\n }\n });\n\n return el as string[];\n // ? return el as (string | string[])[];\n }\n\n if (!Array.isArray(el)) {\n const c = convertChordToNotes(el);\n if (c?.length) {\n return c;\n }\n }\n\n throw new Error(`Chord ${el} not found`);\n};\n\n/** Return a random integer from 0 to `num` (inclusive). */\nexport const randomInt = (num = 1): number => Math.round(Math.random() * num);\n","import type { ClipParams } from './types';\nimport { convertChordsToNotes, shuffle } from './utils';\n\nconst defaultParams: ClipParams = {\n notes: ['C4'],\n pattern: 'x',\n shuffle: false,\n sizzle: false,\n sizzleReps: 1,\n arpegiate: false,\n subdiv: '4n',\n amp: 100,\n accentLow: 70,\n randomNotes: null,\n offlineRendering: false,\n};\n\n/** Throw if the pattern string contains characters outside `x - _ [ ] R`. */\nexport const validatePattern = (pattern: string): void => {\n if (/[^x\\-_[\\]R]/.test(pattern)) {\n throw new TypeError(\n `pattern can only comprise x - _ [ ] R, found ${pattern}`\n );\n }\n};\n\n/**\n * Merge defaults into clip params, normalize notes/randomNotes from strings\n * to arrays, validate the pattern, and optionally shuffle notes.\n */\nexport const preprocessClipParams = (\n params: ClipParams,\n extraDefaults?: Partial<ClipParams>\n): ClipParams => {\n params = { ...defaultParams, ...extraDefaults, ...(params || {}) };\n\n // Notes: string → array\n if (typeof params.notes === 'string') {\n params.notes = params.notes.replace(/\\s{2,}/g, ' ').split(' ');\n }\n params.notes = params.notes ? params.notes.map(convertChordsToNotes) : [];\n\n // Pattern validation\n validatePattern(params.pattern);\n\n // Shuffle\n if (params.shuffle) {\n params.notes = shuffle(params.notes);\n }\n\n // Random notes preprocessing\n if (params.randomNotes && typeof params.randomNotes === 'string') {\n params.randomNotes = params.randomNotes.replace(/\\s{2,}/g, ' ').split(/\\s/);\n }\n if (params.randomNotes) {\n params.randomNotes = (params.randomNotes as string[]).map(\n convertChordsToNotes\n );\n }\n\n return params;\n};\n","import { preprocessClipParams } from './clip-utils';\nimport type {\n ClipParams,\n NoteObject,\n NVP,\n PatternElement,\n SizzleStyle,\n} from './types';\nimport { expandStr, randomInt } from './utils';\n\n/**\n * HDR speed is denoted by the number of ticks per note\n * By default this is set to a quarter note (4n) to be in line with Tone.js' default subdivision\n * Technically a bar is 512 ticks long. So it's HDR speed is 512\n * @type {Object}\n */\nconst hdr: NVP<number> = {\n '1m': 2048,\n '2m': 4096,\n '3m': 6144,\n '4m': 8192,\n '1n': 512,\n '2n': 256,\n '4n': 128,\n '8n': 64,\n '16n': 32,\n};\n\n/**\n * Generate an array of note objects from clip parameters (for MIDI export).\n * Applies the pattern to notes, then optionally adds sizzle and accent dynamics.\n */\nexport const clip = (params: ClipParams): NoteObject[] => {\n params = preprocessClipParams(params);\n\n const clipNotes: NoteObject[] = [];\n let step = 0;\n /**\n * Recursively apply pattern to notes\n *\n * Pass in a pattern array such as ['x', '-', 'x', 'x'] with a length for each element\n * The length is the HDR speed or tick length (obtained from the hdr object in this script)\n * If the element of this array is also a (pattern) array, then divide the length by\n * the length of the inner array and then call the recursive function on that inner array\n */\n const recursivelyApplyPatternToNotes = (\n patternArr: PatternElement[],\n length: number,\n parentNoteLength: number | boolean\n ) => {\n let totalLength = 0;\n patternArr.forEach((char, idx) => {\n if (typeof char === 'string') {\n let note: string[] | null = null;\n\n if (char === '-') {\n // note = null;\n } else if (\n char === 'R' &&\n randomInt() && // Use 1/2 probability for R to pick from param.notes\n params.randomNotes &&\n params.randomNotes.length > 0\n ) {\n note = params.randomNotes[\n randomInt(params.randomNotes.length - 1)\n ] as string[];\n } else if (params.notes) {\n note = params.notes[step] as string[];\n }\n\n if (char === 'x' || char === 'R') {\n step++;\n }\n\n // Push only note on OR off messages to the clip notes array\n if (char === 'x' || char === '-' || char === 'R') {\n clipNotes.push({\n note,\n length,\n level:\n char === 'R' && !params.randomNotes\n ? (params.accentLow as number)\n : (params.amp as number),\n });\n totalLength += length;\n }\n\n // In case of an underscore, simply extend the previous note's length\n if (char === '_' && clipNotes.length) {\n clipNotes[clipNotes.length - 1].length += length;\n totalLength += length;\n }\n\n // if there were triplets in this iteration then ajust length of the last note\n if (\n parentNoteLength &&\n totalLength !== parentNoteLength &&\n idx === patternArr.length - 1\n ) {\n const diff: number = Math.abs(\n (parentNoteLength as number) - totalLength\n );\n const lastClipNote = clipNotes[clipNotes.length - 1];\n if (lastClipNote.length > diff) {\n lastClipNote.length = lastClipNote.length - diff;\n } else {\n lastClipNote.length = lastClipNote.length + diff;\n }\n }\n\n // If the pattern is longer than the notes, then repeat notes\n if (step === params.notes?.length) {\n step = 0;\n }\n }\n // Note: The following condition is not in a else if simply because\n // we do need to increment the totalLength in order to support triplets\n if (Array.isArray(char)) {\n let isTriplet = false;\n // either this is a triplet or not\n if (char.length % 2 !== 0 || length % 2 !== 0) {\n isTriplet = true;\n }\n recursivelyApplyPatternToNotes(\n char,\n Math.round(length / char.length),\n isTriplet && length\n );\n // Increment total length to support subsequent operations\n // once we are out of the recursion\n totalLength += length;\n }\n });\n };\n\n recursivelyApplyPatternToNotes(\n expandStr(params.pattern),\n hdr[params.subdiv as string] || hdr['4n'],\n false\n );\n\n // Many thanks to @R1G for the following functionality\n if (params.sizzle) {\n const volArr = [];\n const style: SizzleStyle = params.sizzle === true ? 'sin' : params.sizzle;\n const beats: number = clipNotes.length;\n const amp: number = params.amp as number;\n const sizzleReps = params.sizzleReps as number;\n const stepLevel = amp / (beats / sizzleReps);\n if (style === 'sin' || style === 'cos') {\n for (let i = 0; i < beats; i++) {\n const level = Math[style]((i * Math.PI) / (beats / sizzleReps)) * amp;\n volArr.push(Math.round(Math.abs(level)));\n }\n }\n\n if (style === 'rampUp') {\n let level = 0;\n for (let i = 0; i < beats; i++) {\n if (i % (beats / sizzleReps) === 0) {\n level = 0;\n } else {\n level = level + stepLevel;\n }\n volArr.push(Math.round(Math.abs(level)));\n }\n }\n\n if (style === 'rampDown') {\n let level = amp;\n for (let i = 0; i < beats; i++) {\n if (i % (beats / sizzleReps) === 0) {\n level = amp;\n } else {\n level = level - stepLevel;\n }\n volArr.push(Math.round(Math.abs(level)));\n }\n }\n\n for (let i = 0; i < volArr.length; i++) {\n clipNotes[i].level = volArr[i] ? volArr[i] : 1; // Cannot allow 0 value on level\n }\n }\n\n if (params.accent) {\n // TODO: Eslint barks at \\- as useless, need to verify that JS handles - without \\ properly.\n if (/[^x-]/.test(params.accent)) {\n throw new TypeError('Accent can only have x and - characters');\n }\n\n let a = 0;\n for (const clipNote of clipNotes) {\n let level =\n params.accent[a] === 'x'\n ? (params.amp as number)\n : (params.accentLow as number);\n\n if (params.sizzle) {\n level = (clipNote.level + level) / 2;\n }\n\n clipNote.level = Math.round(level);\n\n // Step to the next character in the accent\n a = a + 1;\n\n // Reset `a` so that it can loop over the accent\n if (a === params.accent.length) {\n a = 0;\n }\n }\n }\n\n return clipNotes;\n};\n","import fs from 'node:fs';\nimport { File, Track } from '@scribbletune/midi';\nimport type { NoteObject } from './types';\n\n/**\n * Take an array of note objects to generate a MIDI file in the same location as this method is called\n * @param {NoteObject[]} notes Notes are in the format: {note: ['c3'], level: 127, length: 64}\n * @param {String | null} fileName If a filename is not provided, then `music.mid` is used by default\n * If `null` is passed for `fileName`, bytes are returned instead of creating a file\n * If this method is called from a browser then it will return a HTML link that you can append in your page\n * This link will enable the generated MIDI as a downloadable file.\n * @param {Number | null} bpm If a value is provided, the generated midi file will be set to this bpm value.\n */\nexport const midi = (\n notes: NoteObject[],\n fileName: string | null = 'music.mid',\n bpm?: number\n): string | HTMLAnchorElement | undefined => {\n const file = createFileFromNotes(notes, bpm);\n const bytes = file.toBytes();\n\n if (fileName === null) {\n return bytes;\n }\n\n if (!fileName.endsWith('.mid')) {\n fileName = `${fileName}.mid`;\n }\n\n if (\n typeof window !== 'undefined' &&\n window.URL &&\n typeof window.URL.createObjectURL === 'function'\n ) {\n return createDownloadLink(bytes, fileName);\n }\n\n fs.writeFileSync(fileName, bytes, 'binary');\n console.log(`MIDI file generated: ${fileName}.`);\n};\n\n/**\n * Create a downloadable link\n * @param b\n */\nconst createDownloadLink = (b: string, fileName: string): HTMLAnchorElement => {\n // Convert bytes to array buffer\n // Accepted answer on https://stackoverflow.com/questions/35038884/download-file-from-bytes-in-javascript\n const bytes = new Uint8Array(b.length);\n for (let i = 0; i < b.length; i++) {\n const ascii = b.charCodeAt(i);\n bytes[i] = ascii;\n }\n\n // Create a Blob so that we can set it up with the type of file we want (for eg MIDI)\n const blob = new Blob([bytes], { type: 'audio/midi' });\n\n // Create a link element to be used (you can use an existing link on the page as well)\n const link = document.createElement('a');\n link.href =\n (typeof window !== 'undefined' &&\n typeof window.URL !== 'undefined' &&\n typeof window.URL.createObjectURL !== 'undefined' &&\n window.URL.createObjectURL(blob)) ||\n '';\n\n // Give the downloadable file a name\n link.download = fileName;\n link.innerText = 'Download MIDI file';\n\n return link;\n};\n\n/** Build a MIDI File with a single track from the given note objects. */\nconst createFileFromNotes = (notes: NoteObject[], bpm?: number) => {\n const file = new File();\n const track = new Track();\n\n // set the track's bpm if it is provided\n if (typeof bpm === 'number') {\n track.setTempo(bpm);\n }\n\n file.addTrack(track);\n\n for (const noteObj of notes) {\n const level = noteObj.level || 127;\n // While writing chords (multiple notes per tick)\n // only the first noteOn (or noteOff) needs the complete arity of the function call\n // subsequent calls need only the first 2 args (channel and note)\n if (noteObj.note) {\n if (typeof noteObj.note === 'string') {\n track.noteOn(0, noteObj.note, noteObj.length, level); // channel, pitch(note), length, velocity\n track.noteOff(0, noteObj.note, noteObj.length, level);\n } else {\n track.addChord(0, noteObj.note, noteObj.length, level);\n }\n } else {\n track.noteOff(0, '', noteObj.length);\n }\n }\n\n return file;\n};\n","import { scale } from 'harmonics';\nimport type { NVP, ProgressionScale, TPD } from './types';\nimport { dice, pickOne } from './utils';\n\n/**\n * Get the chords that go with a given scale/mode\n * This is useful only in case you want to check what chords work with a scale/mode\n * so that you can come up with chord progressions\n * @param {String} mode e.g. major\n * @return {Array} e.g.['I', 'ii', 'iii', 'IV', 'V', 'vi', 'vii°']\n */\nexport const getChordDegrees = (mode: string): string[] => {\n const theRomans: NVP<string[]> = {\n ionian: ['I', 'ii', 'iii', 'IV', 'V', 'vi', 'vii°'],\n dorian: ['i', 'ii', 'III', 'IV', 'v', 'vi°', 'VII'],\n phrygian: ['i', 'II', 'III', 'iv', 'v°', 'VI', 'vii'],\n lydian: ['I', 'II', 'iii', 'iv°', 'V', 'vi', 'vii'],\n mixolydian: ['I', 'ii', 'iii°', 'IV', 'v', 'vi', 'VII'],\n aeolian: ['i', 'ii°', 'III', 'iv', 'v', 'VI', 'VII'],\n locrian: ['i°', 'II', 'iii', 'iv', 'V', 'VI', 'vii'],\n 'melodic minor': ['i', 'ii', 'III+', 'IV', 'V', 'vi°', 'vii°'],\n 'harmonic minor': ['i', 'ii°', 'III+', 'iv', 'V', 'VI', 'vii°'],\n };\n theRomans.major = theRomans.ionian;\n theRomans.minor = theRomans.aeolian;\n\n return theRomans[mode] || [];\n};\n\nconst idxByDegree: NVP<number> = {\n i: 0,\n ii: 1,\n iii: 2,\n iv: 3,\n v: 4,\n vi: 5,\n vii: 6,\n};\n\n/**\n * Get a chord name from degree\n * @param {String} roman e.g. ii OR ii° OR V7\n * @return {String} e.g. m OR m7b5 OR Maj7\n */\nconst getChordName = (roman: string): string => {\n // remove any non character\n const str = roman.replace(/\\W/g, '');\n let prefix = 'M';\n // check if it s lowercase\n if (str.toLowerCase() === str) {\n prefix = 'm';\n }\n if (roman.indexOf('°') > -1) {\n return `${prefix}7b5`;\n }\n if (roman.indexOf('+') > -1) {\n return `${prefix}#5`;\n }\n\n if (roman.indexOf('7') > -1) {\n return prefix === 'M' ? 'maj7' : 'm7';\n }\n\n return prefix;\n};\n\n/**\n * Take the specified scale and degrees and return the chord names for them\n * These can be used as the value for the `notes` param of the `clip` method\n * @param {String} noteOctaveScale e.g. 'C4 major'\n * @param {String} chordDegress e.g. 'I IV V IV'\n * @return {String} e.g. 'CM FM GM FM'\n */\nexport const getChordsByProgression = (\n noteOctaveScale: string,\n chordDegress: string\n): string => {\n // Set the octave if missing\n // For example if the method was called with `C major` instead of `C4 major`, then add the 4\n const noteOctaveScaleArr = noteOctaveScale.split(' ');\n if (!noteOctaveScaleArr[0].match(/\\d/)) {\n noteOctaveScaleArr[0] += '4';\n noteOctaveScale = noteOctaveScaleArr.join(' ');\n }\n\n // Get the scale from the given note and scale/mode combination\n const mode = scale(noteOctaveScale);\n const chordDegreesArr = chordDegress.replace(/\\s*,+\\s*/g, ' ').split(' ');\n // Now we have something like ['i', 'ii', 'IV']\n // Convert it to a chord family such as ['Cm', 'Dm', 'FM']\n const chordFamily = chordDegreesArr.map(roman => {\n const chordName = getChordName(roman); // e.g. m\n // get the index to be used by removing any digit or non alphabet character\n const scaleId = idxByDegree[roman.replace(/\\W|\\d/g, '').toLowerCase()]; // e.g. 0\n // get the note itself\n const note = mode[scaleId]; // e.g. C\n // get the octave of the note;\n const oct = note.replace(/\\D+/, ''); // e.g. 4\n // now get the chord\n return `${note.replace(/\\d/, '') + chordName}_${oct}`;\n });\n\n return chordFamily.toString().replace(/,/g, ' ');\n};\n\n/** Create a progression generator that follows tonic -> predominant -> dominant flow. */\nconst getProgFactory = ({ T, P, D }: TPD) => {\n return (count = 4) => {\n const chords = [];\n\n // Push root/tonic\n chords.push(pickOne(T));\n\n let i = 1;\n\n // Pick a predominant\n if (i < count - 1) {\n chords.push(pickOne(P));\n i++;\n }\n\n // Try another predominant\n if (i < count - 1 && dice()) {\n chords.push(pickOne(P));\n i++;\n }\n\n // /////// 4 or more//////////\n if (i < count - 1) {\n // Pick a dominant\n chords.push(pickOne(D));\n i++;\n }\n\n if (i < count - 1) {\n // Pick a predominant\n chords.push(pickOne(P));\n i++;\n }\n\n if (i < count - 1) {\n // Pick a dominant\n chords.push(pickOne(D));\n i++;\n }\n\n // Pick a predominant if possible\n if (i < count - 1 && dice()) {\n chords.push(pickOne(P));\n i++;\n }\n // //////////////////////////\n\n // Fill the rest with dominant\n while (i < count) {\n chords.push(pickOne(D));\n i++;\n }\n\n return chords;\n };\n};\n\nconst M = getProgFactory({ T: ['I', 'vi'], P: ['ii', 'IV'], D: ['V'] });\nconst m = getProgFactory({ T: ['i', 'VI'], P: ['ii', 'iv'], D: ['V'] });\n\n/**\n * Generate a chord progression based on basic music theory\n * where we follow tonic to optionally predominant and then dominant\n * and then randomly to predominant and continue this till we reach `count`\n * @param scaleType e.g. M (for major chord progression), m (for minor chord progression)\n * @param count e.g. 4\n */\nexport const progression = (\n scaleType: ProgressionScale,\n count = 4\n): string[] => {\n if (scaleType === 'major' || scaleType === 'M') {\n return M(count);\n }\n\n if (scaleType === 'minor' || scaleType === 'm') {\n return m(count);\n }\n\n return [];\n};\n"],"mappings":"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","names":["import_harmonics","import_harmonics","fs","import_harmonics","chords"]}
|