scribbletune 5.5.1 → 5.5.2
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/README.md +27 -27
- package/dist/cli.cjs +45 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<h1 align="center">Scribbletune</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
Create music with JavaScript. Use simple strings and arrays to craft rhythms, melodies, and chord progressions — then export MIDI files or play them live in the browser with <a href="https://tonejs.github.io/">Tone.js</a
|
|
8
|
+
Create music with JavaScript. Use simple strings and arrays to craft rhythms, melodies, and chord progressions — then export MIDI files or play them live in the browser with <a href="https://tonejs.github.io/">Tone.js</a> or use the CLI to directly emit MIDI file from your terminal.
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
@@ -23,9 +23,9 @@ npm install scribbletune
|
|
|
23
23
|
|
|
24
24
|
## Quick start
|
|
25
25
|
|
|
26
|
-
### CLI
|
|
26
|
+
### Option 1: CLI
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
If you installed Scribbletune globally via `npm i -g scribbletune` then you can directly use `scribbletune` as the command. If you installed it locally via `npm i scribbletune` then please use `npx scribbletune` as the command.
|
|
29
29
|
|
|
30
30
|
Run modes:
|
|
31
31
|
|
|
@@ -41,21 +41,10 @@ npx scribbletune --help
|
|
|
41
41
|
|
|
42
42
|
Quick command examples:
|
|
43
43
|
|
|
44
|
-
```bash
|
|
45
|
-
# Use built file directly from repo (before publish)
|
|
46
|
-
node dist/cli.cjs --help
|
|
47
|
-
|
|
48
|
-
# Use local package binary
|
|
49
|
-
npx scribbletune --help
|
|
50
|
-
|
|
51
|
-
# If installed globally
|
|
52
|
-
scribbletune --help
|
|
53
|
-
```
|
|
54
|
-
|
|
55
44
|
#### Command format
|
|
56
45
|
|
|
57
46
|
```bash
|
|
58
|
-
scribbletune --riff <root> <mode> <pattern> [
|
|
47
|
+
scribbletune --riff <root> <mode> <pattern> [subdiv] [options]
|
|
59
48
|
scribbletune --chord <root> <mode> <progression|random> <pattern> [subdiv] [options]
|
|
60
49
|
scribbletune --arp <root> <mode> <progression|random> <pattern> [subdiv] [options]
|
|
61
50
|
```
|
|
@@ -67,26 +56,23 @@ Progression input rules for `--chord` and `--arp`:
|
|
|
67
56
|
"I IV vi V" # roman numerals (space separated)
|
|
68
57
|
I,IV,vi,V # roman numerals (comma separated)
|
|
69
58
|
random # generated progression
|
|
70
|
-
CM-FM-Am-GM # explicit chord names
|
|
59
|
+
CM-FM-Am-GM # explicit chord names (`root` and `mode` are ignored)
|
|
71
60
|
```
|
|
72
61
|
|
|
73
|
-
Notes:
|
|
74
|
-
- Hyphenated romans like `I-IV-vi-V` are not supported currently.
|
|
75
|
-
- For explicit chords (`CM-FM-Am-GM`), `root` and `mode` are currently ignored.
|
|
76
|
-
|
|
77
62
|
Common options:
|
|
78
63
|
|
|
79
64
|
```bash
|
|
80
|
-
--outfile <file.mid>
|
|
81
|
-
--bpm <number>
|
|
65
|
+
--outfile <file.mid> # default: music.mid
|
|
82
66
|
--subdiv <4n|8n|1m...>
|
|
83
67
|
--sizzle [sin|cos|rampUp|rampDown] [reps]
|
|
84
68
|
--sizzle-reps <number>
|
|
85
69
|
--amp <0-127>
|
|
86
70
|
--accent <x--x...>
|
|
87
71
|
--accent-low <0-127>
|
|
72
|
+
--style <letters> # riff motif/style, e.g. AABC
|
|
88
73
|
--fit-pattern # explicit enable (already enabled by default)
|
|
89
74
|
--no-fit-pattern # disable automatic pattern fitting
|
|
75
|
+
--bpm <number> # your DAW may or may not support it
|
|
90
76
|
```
|
|
91
77
|
|
|
92
78
|
Note: if your pattern uses `[` and `]` (for subdivisions), quote it in shell:
|
|
@@ -110,13 +96,21 @@ x.repeat(4) # -> xxxx
|
|
|
110
96
|
# Basic riff from scale
|
|
111
97
|
scribbletune --riff C3 phrygian x-xRx_RR --outfile riff.mid
|
|
112
98
|
|
|
113
|
-
# With
|
|
114
|
-
scribbletune --riff C3 phrygian x-xRx_RR
|
|
99
|
+
# With motif/style and positional subdiv
|
|
100
|
+
scribbletune --riff C3 phrygian x-xRx_RR 8n --style AABC --sizzle sin 2 --outfile riff-aabc.mid
|
|
101
|
+
|
|
102
|
+
# Set riff subdivision via positional arg
|
|
103
|
+
scribbletune --riff C3 phrygian x-xRx_RR 8n --style AABC --outfile riff-8n.mid
|
|
115
104
|
|
|
116
105
|
# Pattern with subdivisions (quote [] in shell)
|
|
117
|
-
scribbletune --riff C3 phrygian 'x-x[xx]-x-[xx]'
|
|
106
|
+
scribbletune --riff C3 phrygian 'x-x[xx]-x-[xx]' 8n --style AABC --outfile riff-subdiv.mid
|
|
118
107
|
```
|
|
119
108
|
|
|
109
|
+
Riff + motif note:
|
|
110
|
+
- `--style` creates riff sections by repeating the full pattern per letter.
|
|
111
|
+
- Example: `--style AABC` with pattern `x-x[xx]` creates 4 sections: `A`, `A`, `B`, `C`.
|
|
112
|
+
- Repeated letters reuse the exact same generated section (same rhythm and same notes, including random `R` choices).
|
|
113
|
+
|
|
120
114
|
#### `--chord` examples
|
|
121
115
|
|
|
122
116
|
```bash
|
|
@@ -167,7 +161,7 @@ scribbletune --arp C3 major 1736 x 4n --no-fit-pattern --outfile arp-no-fit.mid
|
|
|
167
161
|
|
|
168
162
|
Run `scribbletune --help` to see the latest CLI usage text.
|
|
169
163
|
|
|
170
|
-
###
|
|
164
|
+
### Option 2: Node.js
|
|
171
165
|
|
|
172
166
|
```js
|
|
173
167
|
import { scale, clip, midi } from 'scribbletune';
|
|
@@ -180,7 +174,7 @@ midi(c, 'c-major.mid');
|
|
|
180
174
|
|
|
181
175
|
Run it with `node` and open the `.mid` file in Ableton Live, GarageBand, Logic, or any DAW.
|
|
182
176
|
|
|
183
|
-
###
|
|
177
|
+
### Option 3: Browser (with Tone.js)
|
|
184
178
|
|
|
185
179
|
Scribbletune's browser entry point adds `Session`, `Channel`, and live `clip()` support on top of [Tone.js](https://tonejs.github.io/).
|
|
186
180
|
|
|
@@ -355,6 +349,12 @@ npm run lint # check with biome
|
|
|
355
349
|
npm run dev # build in watch mode
|
|
356
350
|
```
|
|
357
351
|
|
|
352
|
+
If developing new features for the CLI, use the following command after running `npm run build` to test before publishing,
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
node dist/cli.cjs --help
|
|
356
|
+
```
|
|
357
|
+
|
|
358
358
|
## License
|
|
359
359
|
|
|
360
360
|
MIT
|
package/dist/cli.cjs
CHANGED
|
@@ -546,12 +546,12 @@ var progression = (scaleType, count = 4) => {
|
|
|
546
546
|
|
|
547
547
|
// src/cli.ts
|
|
548
548
|
var HELP_TEXT = `Usage:
|
|
549
|
-
scribbletune --riff <root> <mode> <pattern> [
|
|
549
|
+
scribbletune --riff <root> <mode> <pattern> [subdiv]
|
|
550
550
|
scribbletune --chord <root> <mode> <progression|random> <pattern> [subdiv]
|
|
551
551
|
scribbletune --arp <root> <mode> <progression|random> <pattern> [subdiv]
|
|
552
552
|
|
|
553
553
|
Examples:
|
|
554
|
-
scribbletune --riff C3 phrygian x-xRx_RR
|
|
554
|
+
scribbletune --riff C3 phrygian x-xRx_RR 8n --style AABC --sizzle sin 2 --outfile riff.mid
|
|
555
555
|
scribbletune --chord C3 major 1645 xxxx 1m --sizzle cos 1 --outfile chord.mid
|
|
556
556
|
scribbletune --chord C3 major CM-FM-Am-GM xxxx 1m
|
|
557
557
|
scribbletune --chord C3 major random xxxx 1m
|
|
@@ -568,6 +568,7 @@ Options:
|
|
|
568
568
|
--accent-low <0-127> Accent low level
|
|
569
569
|
--count <2-8> Arp note count (arp command only)
|
|
570
570
|
--order <digits> Arp order string (arp command only)
|
|
571
|
+
--style <letters> Riff motif/style pattern (e.g. AABC)
|
|
571
572
|
--fit-pattern Repeat pattern until it can consume all generated notes (default)
|
|
572
573
|
--no-fit-pattern Disable automatic pattern fitting
|
|
573
574
|
-h, --help Show this help
|
|
@@ -587,11 +588,6 @@ var romanByDigit = (progDigits, mode) => {
|
|
|
587
588
|
});
|
|
588
589
|
return { chordDegrees: romans.join(" "), raw: progDigits };
|
|
589
590
|
};
|
|
590
|
-
var setOctave = (note, octaveShift = 0) => {
|
|
591
|
-
const base = note.replace(/\d+/g, "");
|
|
592
|
-
const oct = Number(note.match(/\d+/)?.[0] || "4");
|
|
593
|
-
return `${base}${oct + octaveShift}`;
|
|
594
|
-
};
|
|
595
591
|
var parseProgression = (root, mode, progressionInput) => {
|
|
596
592
|
if (progressionInput === "random") {
|
|
597
593
|
const modeType = mode === "minor" || mode === "m" ? "minor" : "major";
|
|
@@ -751,6 +747,11 @@ var parseCliArgs = (argv) => {
|
|
|
751
747
|
i += 2;
|
|
752
748
|
continue;
|
|
753
749
|
}
|
|
750
|
+
if (token === "--style") {
|
|
751
|
+
options.style = argv[i + 1];
|
|
752
|
+
i += 2;
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
754
755
|
if (token === "--fit-pattern") {
|
|
755
756
|
options.fitPattern = true;
|
|
756
757
|
i += 1;
|
|
@@ -775,31 +776,53 @@ var baseClipParams = (parsed) => {
|
|
|
775
776
|
};
|
|
776
777
|
};
|
|
777
778
|
var makeRiff = (parsed) => {
|
|
778
|
-
const [root, mode, pattern,
|
|
779
|
+
const [root, mode, pattern, subdiv] = parsed.positionals;
|
|
780
|
+
const style = parsed.style;
|
|
779
781
|
if (!root || !mode || !pattern) {
|
|
780
|
-
throw new TypeError(
|
|
781
|
-
"riff requires: <root> <mode> <pattern> [octaveShift] [motif]"
|
|
782
|
-
);
|
|
782
|
+
throw new TypeError("riff requires: <root> <mode> <pattern> [subdiv]");
|
|
783
783
|
}
|
|
784
|
-
const
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
const idx = letter.charCodeAt(0) - 65;
|
|
788
|
-
if (idx < 0) {
|
|
789
|
-
return riffScale[0];
|
|
790
|
-
}
|
|
791
|
-
return riffScale[idx % riffScale.length];
|
|
792
|
-
}) : riffScale;
|
|
793
|
-
const resolvedPattern = resolvePattern(
|
|
784
|
+
const riffScale = (0, import_harmonics4.scale)(`${root} ${mode}`);
|
|
785
|
+
let riffNotes = riffScale;
|
|
786
|
+
let resolvedPattern = resolvePattern(
|
|
794
787
|
pattern,
|
|
795
788
|
riffNotes.length,
|
|
796
789
|
parsed.fitPattern
|
|
797
790
|
);
|
|
791
|
+
if (style && style.length) {
|
|
792
|
+
const sectionPattern = expandPatternSyntax(pattern);
|
|
793
|
+
const letters = style.toUpperCase().split("");
|
|
794
|
+
const sectionCache = {};
|
|
795
|
+
const combined = [];
|
|
796
|
+
const clipParams = {
|
|
797
|
+
...baseClipParams(parsed),
|
|
798
|
+
subdiv: parsed.subdiv || subdiv
|
|
799
|
+
};
|
|
800
|
+
for (const letter of letters) {
|
|
801
|
+
if (!sectionCache[letter]) {
|
|
802
|
+
const idx = letter.charCodeAt(0) - 65;
|
|
803
|
+
const note = riffScale[idx >= 0 ? idx % riffScale.length : 0];
|
|
804
|
+
sectionCache[letter] = clip({
|
|
805
|
+
notes: [note],
|
|
806
|
+
randomNotes: riffScale,
|
|
807
|
+
pattern: sectionPattern,
|
|
808
|
+
...clipParams
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
combined.push(
|
|
812
|
+
...sectionCache[letter].map((event) => ({
|
|
813
|
+
...event,
|
|
814
|
+
note: event.note ? [...event.note] : null
|
|
815
|
+
}))
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
return combined;
|
|
819
|
+
}
|
|
798
820
|
return clip({
|
|
799
821
|
notes: riffNotes,
|
|
800
822
|
randomNotes: riffScale,
|
|
801
823
|
pattern: resolvedPattern,
|
|
802
|
-
...baseClipParams(parsed)
|
|
824
|
+
...baseClipParams(parsed),
|
|
825
|
+
subdiv: parsed.subdiv || subdiv
|
|
803
826
|
});
|
|
804
827
|
};
|
|
805
828
|
var makeChord = (parsed) => {
|