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.
Files changed (3) hide show
  1. package/README.md +27 -27
  2. package/dist/cli.cjs +45 -22
  3. 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
- The package now ships a CLI binary: `scribbletune`.
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> [octaveShift] [motif] [options]
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> # default: music.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 octave shift and motif
114
- scribbletune --riff C3 phrygian x-xRx_RR 0 AABC --sizzle sin 2 --outfile riff-aabc.mid
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]' 0 AABC --outfile riff-subdiv.mid
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
- ### Generate a MIDI file (Node.js)
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
- ### Play in the browser (with Tone.js)
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> [octaveShift] [motif]
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 0 AABC --sizzle sin 2 --outfile riff.mid
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, octaveShiftArg, motif] = parsed.positionals;
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 octaveShift = Number(octaveShiftArg || "0");
785
- const riffScale = (0, import_harmonics4.scale)(`${setOctave(root, octaveShift)} ${mode}`);
786
- const riffNotes = motif && motif.length ? motif.toUpperCase().split("").map((letter) => {
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) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scribbletune",
3
- "version": "5.5.1",
3
+ "version": "5.5.2",
4
4
  "description": "Create music with JavaScript and Node.js!",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",