@williamzujkowski/live-coding-music-mcp 2.0.0 → 4.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.
@@ -1,245 +0,0 @@
1
- import { MusicTheory } from '../services/MusicTheory';
2
- describe('MusicTheory', () => {
3
- let theory;
4
- beforeEach(() => {
5
- theory = new MusicTheory();
6
- });
7
- describe('generateScale', () => {
8
- test('should generate correct major scale', () => {
9
- const scale = theory.generateScale('C', 'major');
10
- expect(scale).toEqual(['C', 'D', 'E', 'F', 'G', 'A', 'B']);
11
- });
12
- test('should generate correct minor scale', () => {
13
- const scale = theory.generateScale('A', 'minor');
14
- expect(scale).toEqual(['A', 'B', 'C', 'D', 'E', 'F', 'G']);
15
- });
16
- test('should generate correct dorian scale', () => {
17
- const scale = theory.generateScale('D', 'dorian');
18
- expect(scale).toEqual(['D', 'E', 'F', 'G', 'A', 'B', 'C']);
19
- });
20
- test('should generate correct pentatonic scale', () => {
21
- const scale = theory.generateScale('C', 'pentatonic');
22
- expect(scale).toEqual(['C', 'D', 'E', 'G', 'A']);
23
- });
24
- test('should generate correct blues scale', () => {
25
- const scale = theory.generateScale('C', 'blues');
26
- expect(scale).toEqual(['C', 'D#', 'F', 'F#', 'G', 'A#']);
27
- });
28
- test('should generate all modal scales correctly', () => {
29
- const modes = ['dorian', 'phrygian', 'lydian', 'mixolydian', 'aeolian', 'locrian'];
30
- modes.forEach(mode => {
31
- const scale = theory.generateScale('C', mode);
32
- expect(scale).toHaveLength(7);
33
- expect(scale[0]).toBe('C');
34
- });
35
- });
36
- test('should handle sharps correctly', () => {
37
- const scale = theory.generateScale('F#', 'major');
38
- expect(scale).toContain('F#');
39
- expect(scale).toHaveLength(7);
40
- });
41
- test('should throw error for invalid root note', () => {
42
- expect(() => theory.generateScale('X', 'major')).toThrow('Invalid root note: X');
43
- });
44
- test('should throw error for invalid scale type', () => {
45
- expect(() => theory.generateScale('C', 'invalid')).toThrow('Invalid scale: invalid');
46
- });
47
- test('should handle all chromatic notes', () => {
48
- const notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
49
- notes.forEach(note => {
50
- const scale = theory.generateScale(note, 'major');
51
- expect(scale).toHaveLength(7);
52
- expect(scale[0]).toBe(note);
53
- });
54
- });
55
- });
56
- describe('generateChordProgression', () => {
57
- test('should generate pop progression (I-V-vi-IV)', () => {
58
- const progression = theory.generateChordProgression('C', 'pop');
59
- expect(progression).toBe('"C" "G" "Am" "F"');
60
- });
61
- test('should generate jazz progression', () => {
62
- const progression = theory.generateChordProgression('C', 'jazz');
63
- expect(progression).toBe('"Dm7" "G7" "Cmaj7"');
64
- });
65
- test('should generate blues progression', () => {
66
- const progression = theory.generateChordProgression('C', 'blues');
67
- expect(progression).toContain('"C7"');
68
- expect(progression.split(' ')).toHaveLength(12); // 12-bar blues
69
- });
70
- test('should handle different keys', () => {
71
- const progressionC = theory.generateChordProgression('C', 'pop');
72
- const progressionG = theory.generateChordProgression('G', 'pop');
73
- expect(progressionC).not.toBe(progressionG);
74
- });
75
- test('should generate all progression types', () => {
76
- const styles = ['pop', 'jazz', 'blues', 'folk', 'rock', 'classical', 'modal', 'edm'];
77
- styles.forEach(style => {
78
- const progression = theory.generateChordProgression('C', style);
79
- expect(progression).toBeTruthy();
80
- expect(progression.length).toBeGreaterThan(0);
81
- });
82
- });
83
- test('should throw error for invalid style', () => {
84
- expect(() => theory.generateChordProgression('C', 'invalid'))
85
- .toThrow('Invalid progression style: invalid');
86
- });
87
- });
88
- describe('getNote', () => {
89
- test('should transpose notes correctly', () => {
90
- expect(theory.getNote('C', 0)).toBe('C');
91
- expect(theory.getNote('C', 2)).toBe('D');
92
- expect(theory.getNote('C', 4)).toBe('E');
93
- expect(theory.getNote('C', 5)).toBe('F');
94
- expect(theory.getNote('C', 7)).toBe('G');
95
- expect(theory.getNote('C', 9)).toBe('A');
96
- expect(theory.getNote('C', 11)).toBe('B');
97
- });
98
- test('should wrap around octave', () => {
99
- expect(theory.getNote('C', 12)).toBe('C');
100
- expect(theory.getNote('C', 13)).toBe('C#');
101
- expect(theory.getNote('C', 24)).toBe('C');
102
- });
103
- test('should handle sharps', () => {
104
- expect(theory.getNote('C#', 2)).toBe('D#');
105
- expect(theory.getNote('F#', 5)).toBe('B');
106
- });
107
- test('should return original note for invalid root', () => {
108
- expect(theory.getNote('X', 5)).toBe('X');
109
- });
110
- });
111
- describe('generateEuclideanRhythm', () => {
112
- test('should generate valid Euclidean rhythm', () => {
113
- const rhythm = theory.generateEuclideanRhythm(3, 8);
114
- const hits = rhythm.split(' ').filter(x => x === '1').length;
115
- expect(hits).toBe(3);
116
- });
117
- test('should handle different hit/step combinations', () => {
118
- const testCases = [
119
- { hits: 3, steps: 8 },
120
- { hits: 5, steps: 8 },
121
- { hits: 5, steps: 16 },
122
- { hits: 7, steps: 16 },
123
- ];
124
- testCases.forEach(({ hits, steps }) => {
125
- const rhythm = theory.generateEuclideanRhythm(hits, steps);
126
- const actualHits = rhythm.split(' ').filter(x => x === '1').length;
127
- expect(actualHits).toBe(hits);
128
- expect(rhythm.split(' ')).toHaveLength(steps);
129
- });
130
- });
131
- test('should distribute hits evenly', () => {
132
- const rhythm = theory.generateEuclideanRhythm(4, 16);
133
- const steps = rhythm.split(' ');
134
- expect(steps).toHaveLength(16);
135
- // Check that hits are evenly distributed (every 4 steps for 4 hits in 16 steps)
136
- expect(steps[0]).toBe('1');
137
- expect(steps[4]).toBe('1');
138
- expect(steps[8]).toBe('1');
139
- expect(steps[12]).toBe('1');
140
- });
141
- test('should throw error when hits exceed steps', () => {
142
- expect(() => theory.generateEuclideanRhythm(10, 8))
143
- .toThrow('Hits cannot exceed steps');
144
- });
145
- test('should handle edge cases', () => {
146
- expect(theory.generateEuclideanRhythm(0, 8).split(' ').filter(x => x === '1')).toHaveLength(0);
147
- expect(theory.generateEuclideanRhythm(8, 8).split(' ').filter(x => x === '1')).toHaveLength(8);
148
- expect(theory.generateEuclideanRhythm(1, 16).split(' ').filter(x => x === '1')).toHaveLength(1);
149
- });
150
- });
151
- describe('generatePolyrhythm', () => {
152
- test('should generate polyrhythm with two patterns', () => {
153
- const polyrhythm = theory.generatePolyrhythm(3, 5, 16);
154
- expect(polyrhythm).toContain('[');
155
- expect(polyrhythm).toContain(']');
156
- expect(polyrhythm.split('],').length).toBe(2);
157
- });
158
- test('should use default length of 16', () => {
159
- const polyrhythm = theory.generatePolyrhythm(3, 5);
160
- const patterns = polyrhythm.split('], [');
161
- patterns.forEach(pattern => {
162
- const cleaned = pattern.replace(/[\[\]]/g, '');
163
- expect(cleaned.split(' ')).toHaveLength(16);
164
- });
165
- });
166
- test('should generate different rhythms for different patterns', () => {
167
- const poly1 = theory.generatePolyrhythm(3, 5, 16);
168
- const poly2 = theory.generatePolyrhythm(5, 3, 16);
169
- expect(poly1).not.toBe(poly2);
170
- });
171
- });
172
- describe('getScaleNotes', () => {
173
- test('should return scale notes with octave numbers', () => {
174
- const notes = theory.getScaleNotes('C', 'major', 3);
175
- expect(notes).toEqual(['c3', 'd3', 'e3', 'f3', 'g3', 'a3', 'b3']);
176
- });
177
- test('should handle different octaves', () => {
178
- const notes4 = theory.getScaleNotes('C', 'major', 4);
179
- const notes5 = theory.getScaleNotes('C', 'major', 5);
180
- expect(notes4).toEqual(['c4', 'd4', 'e4', 'f4', 'g4', 'a4', 'b4']);
181
- expect(notes5).toEqual(['c5', 'd5', 'e5', 'f5', 'g5', 'a5', 'b5']);
182
- });
183
- test('should lowercase note names', () => {
184
- const notes = theory.getScaleNotes('C#', 'major', 3);
185
- notes.forEach(note => {
186
- expect(note[0]).toBe(note[0].toLowerCase());
187
- });
188
- });
189
- });
190
- describe('generateArpeggio', () => {
191
- test('should generate upward arpeggio', () => {
192
- const arp = theory.generateArpeggio('C', 'up');
193
- expect(arp).toBeTruthy();
194
- expect(arp.split(' ')).toHaveLength(4);
195
- });
196
- test('should generate downward arpeggio', () => {
197
- const arp = theory.generateArpeggio('C', 'down');
198
- expect(arp).toBeTruthy();
199
- expect(arp.split(' ')).toHaveLength(4);
200
- });
201
- test('should generate up-down arpeggio', () => {
202
- const arp = theory.generateArpeggio('C', 'updown');
203
- expect(arp.split(' ')).toHaveLength(6);
204
- });
205
- test('should generate random arpeggio', () => {
206
- const arp = theory.generateArpeggio('C', 'random');
207
- expect(arp.split(' ')).toHaveLength(8);
208
- });
209
- test('should default to up pattern for invalid pattern type', () => {
210
- const arp = theory.generateArpeggio('C', 'invalid');
211
- expect(arp.split(' ')).toHaveLength(4);
212
- });
213
- test('should handle different chord types', () => {
214
- const chords = ['C', 'Cm', 'C7', 'Cmaj7', 'Cm7'];
215
- chords.forEach(chord => {
216
- const arp = theory.generateArpeggio(chord, 'up');
217
- expect(arp).toBeTruthy();
218
- });
219
- });
220
- });
221
- describe('integration tests', () => {
222
- test('should create complete musical context', () => {
223
- const key = 'C';
224
- const scale = theory.generateScale(key, 'major');
225
- const progression = theory.generateChordProgression(key, 'pop');
226
- const rhythm = theory.generateEuclideanRhythm(4, 16);
227
- expect(scale).toHaveLength(7);
228
- expect(progression).toContain('C');
229
- expect(rhythm.split(' ')).toHaveLength(16);
230
- });
231
- test('should work with all supported scales and progressions', () => {
232
- const scales = ['major', 'minor', 'dorian', 'phrygian', 'lydian', 'mixolydian',
233
- 'aeolian', 'locrian', 'pentatonic', 'blues'];
234
- const progressions = ['pop', 'jazz', 'blues', 'folk', 'rock', 'classical', 'modal', 'edm'];
235
- scales.forEach(scaleName => {
236
- const scale = theory.generateScale('C', scaleName);
237
- expect(scale.length).toBeGreaterThan(0);
238
- });
239
- progressions.forEach(style => {
240
- const progression = theory.generateChordProgression('C', style);
241
- expect(progression.length).toBeGreaterThan(0);
242
- });
243
- });
244
- });
245
- });
@@ -1,337 +0,0 @@
1
- import { PatternGenerator } from '../services/PatternGenerator';
2
- describe('PatternGenerator', () => {
3
- let generator;
4
- beforeEach(() => {
5
- generator = new PatternGenerator();
6
- });
7
- describe('generateDrumPattern', () => {
8
- test('should generate techno drum pattern', () => {
9
- const pattern = generator.generateDrumPattern('techno', 0.5);
10
- expect(pattern).toContain('s(');
11
- expect(pattern).toContain('bd');
12
- });
13
- test('should generate house drum pattern', () => {
14
- const pattern = generator.generateDrumPattern('house', 0.5);
15
- expect(pattern).toContain('s(');
16
- expect(pattern).toContain('hh');
17
- });
18
- test('should handle complexity levels', () => {
19
- const simple = generator.generateDrumPattern('techno', 0.0);
20
- const complex = generator.generateDrumPattern('techno', 1.0);
21
- expect(simple).toBeTruthy();
22
- expect(complex).toBeTruthy();
23
- expect(simple.length).toBeLessThanOrEqual(complex.length);
24
- });
25
- test('should generate all supported drum styles', () => {
26
- const styles = ['techno', 'house', 'dnb', 'breakbeat', 'trap', 'jungle', 'ambient', 'experimental'];
27
- styles.forEach(style => {
28
- const pattern = generator.generateDrumPattern(style, 0.5);
29
- expect(pattern).toBeTruthy();
30
- expect(pattern).toContain('s(');
31
- });
32
- });
33
- test('should default to techno for unknown styles', () => {
34
- const pattern = generator.generateDrumPattern('unknown', 0.5);
35
- expect(pattern).toBeTruthy();
36
- expect(pattern).toContain('bd');
37
- });
38
- test('should handle complexity edge cases', () => {
39
- expect(generator.generateDrumPattern('techno', 0)).toBeTruthy();
40
- expect(generator.generateDrumPattern('techno', 1)).toBeTruthy();
41
- expect(generator.generateDrumPattern('techno', 0.5)).toBeTruthy();
42
- });
43
- });
44
- describe('generateBassline', () => {
45
- test('should generate techno bassline', () => {
46
- const bass = generator.generateBassline('C', 'techno');
47
- expect(bass).toContain('note(');
48
- expect(bass).toContain('C2');
49
- expect(bass).toContain('sawtooth');
50
- });
51
- test('should generate house bassline', () => {
52
- const bass = generator.generateBassline('D', 'house');
53
- expect(bass).toContain('D2');
54
- expect(bass).toContain('sine');
55
- });
56
- test('should generate acid bassline with modulation', () => {
57
- const bass = generator.generateBassline('E', 'acid');
58
- expect(bass).toContain('E2');
59
- expect(bass).toContain('cutoff');
60
- expect(bass).toContain('sine.range');
61
- });
62
- test('should handle all supported bass styles', () => {
63
- const styles = ['techno', 'house', 'dnb', 'acid', 'dub', 'funk', 'jazz', 'ambient'];
64
- styles.forEach(style => {
65
- const bass = generator.generateBassline('C', style);
66
- expect(bass).toContain('note(');
67
- expect(bass).toContain('C');
68
- });
69
- });
70
- test('should work with different keys', () => {
71
- const keys = ['C', 'D', 'E', 'F', 'G', 'A', 'B', 'C#', 'F#'];
72
- keys.forEach(key => {
73
- const bass = generator.generateBassline(key, 'techno');
74
- expect(bass).toContain(key);
75
- });
76
- });
77
- test('should default to techno for unknown styles', () => {
78
- const bass = generator.generateBassline('C', 'unknown');
79
- expect(bass).toContain('sawtooth');
80
- });
81
- });
82
- describe('generateMelody', () => {
83
- test('should generate melody from scale', () => {
84
- const scale = ['c', 'd', 'e', 'f', 'g', 'a', 'b'];
85
- const melody = generator.generateMelody(scale, 8);
86
- expect(melody).toContain('note(');
87
- expect(melody).toContain('triangle');
88
- });
89
- test('should generate melody with specified length', () => {
90
- const scale = ['c', 'd', 'e'];
91
- const melody4 = generator.generateMelody(scale, 4);
92
- const melody8 = generator.generateMelody(scale, 8);
93
- const melody16 = generator.generateMelody(scale, 16);
94
- const notes4 = melody4.match(/[a-g]\d+/g);
95
- const notes8 = melody8.match(/[a-g]\d+/g);
96
- const notes16 = melody16.match(/[a-g]\d+/g);
97
- expect(notes4?.length).toBe(4);
98
- expect(notes8?.length).toBe(8);
99
- expect(notes16?.length).toBe(16);
100
- });
101
- test('should respect octave range', () => {
102
- const scale = ['c', 'd', 'e', 'f', 'g'];
103
- const melody = generator.generateMelody(scale, 8, [4, 5]);
104
- const notes = melody.match(/[a-g](\d+)/g) || [];
105
- notes.forEach(note => {
106
- const octave = parseInt(note.slice(-1));
107
- expect(octave).toBeGreaterThanOrEqual(4);
108
- expect(octave).toBeLessThanOrEqual(5);
109
- });
110
- });
111
- test('should use default parameters', () => {
112
- const scale = ['c', 'd', 'e'];
113
- const melody = generator.generateMelody(scale);
114
- expect(melody).toBeTruthy();
115
- expect(melody).toContain('note(');
116
- });
117
- test('should only use notes from provided scale', () => {
118
- const scale = ['c', 'e', 'g'];
119
- const melody = generator.generateMelody(scale, 8);
120
- const notePattern = /([a-g]#?)\d+/g;
121
- const matches = melody.match(notePattern) || [];
122
- matches.forEach(match => {
123
- const note = match.replace(/\d+/g, '');
124
- expect(scale).toContain(note);
125
- });
126
- });
127
- });
128
- describe('generateChords', () => {
129
- test('should generate triad voicing', () => {
130
- const chords = generator.generateChords('"C" "F" "G"', 'triad');
131
- expect(chords).toContain('note(');
132
- expect(chords).toContain('sawtooth');
133
- expect(chords).toContain('.struct("1 ~ ~ ~")');
134
- });
135
- test('should generate sustained voicing', () => {
136
- const chords = generator.generateChords('"C" "F"', 'sustained');
137
- expect(chords).toContain('.attack(0.5)');
138
- expect(chords).toContain('.release(2)');
139
- });
140
- test('should generate pad voicing', () => {
141
- const chords = generator.generateChords('"C"', 'pad');
142
- expect(chords).toContain('.attack(2)');
143
- expect(chords).toContain('.release(4)');
144
- expect(chords).toContain('.room(0.8)');
145
- });
146
- test('should default to triad for unknown voicing', () => {
147
- const chords = generator.generateChords('"C"', 'unknown');
148
- expect(chords).toContain('.struct("1 ~ ~ ~")');
149
- });
150
- });
151
- describe('generateCompletePattern', () => {
152
- test('should generate complete techno pattern', () => {
153
- const pattern = generator.generateCompletePattern('techno', 'C', 130);
154
- expect(pattern).toContain('// techno');
155
- expect(pattern).toContain('setcpm(130)');
156
- expect(pattern).toContain('stack(');
157
- expect(pattern).toContain('// Drums');
158
- expect(pattern).toContain('// Bass');
159
- expect(pattern).toContain('// Chords');
160
- expect(pattern).toContain('// Melody');
161
- });
162
- test('should generate pattern for different styles', () => {
163
- const styles = ['techno', 'house', 'jazz', 'ambient'];
164
- styles.forEach(style => {
165
- const pattern = generator.generateCompletePattern(style, 'C', 120);
166
- expect(pattern).toContain(`// ${style}`);
167
- expect(pattern).toContain('stack(');
168
- });
169
- });
170
- test('should use correct key', () => {
171
- const pattern = generator.generateCompletePattern('house', 'D', 120);
172
- expect(pattern).toContain('D');
173
- });
174
- test('should set correct BPM', () => {
175
- const pattern140 = generator.generateCompletePattern('dnb', 'C', 140);
176
- const pattern90 = generator.generateCompletePattern('ambient', 'C', 90);
177
- expect(pattern140).toContain('setcpm(140)');
178
- expect(pattern90).toContain('setcpm(90)');
179
- });
180
- test('should use appropriate scale for style', () => {
181
- const jazzPattern = generator.generateCompletePattern('jazz', 'C', 120);
182
- const technoPattern = generator.generateCompletePattern('techno', 'C', 120);
183
- // Jazz should use dorian, others use minor (indirectly verified through note choices)
184
- expect(jazzPattern).toBeTruthy();
185
- expect(technoPattern).toBeTruthy();
186
- });
187
- });
188
- describe('generateVariation', () => {
189
- test('should add subtle variation', () => {
190
- const base = 's("bd*4")';
191
- const varied = generator.generateVariation(base, 'subtle');
192
- expect(varied).toContain(base);
193
- expect(varied).toContain('.sometimes');
194
- });
195
- test('should add moderate variation', () => {
196
- const base = 's("bd*4")';
197
- const varied = generator.generateVariation(base, 'moderate');
198
- expect(varied).toContain('.every(4');
199
- expect(varied).toContain('.sometimes');
200
- });
201
- test('should add extreme variation', () => {
202
- const base = 's("bd*4")';
203
- const varied = generator.generateVariation(base, 'extreme');
204
- expect(varied).toContain('.every(2');
205
- expect(varied).toContain('.jux');
206
- });
207
- test('should add glitch variation', () => {
208
- const base = 's("bd*4")';
209
- const varied = generator.generateVariation(base, 'glitch');
210
- expect(varied).toContain('.chop');
211
- });
212
- test('should add evolving variation', () => {
213
- const base = 's("bd*4")';
214
- const varied = generator.generateVariation(base, 'evolving');
215
- expect(varied).toContain('.slow(4)');
216
- expect(varied).toContain('.palindrome');
217
- });
218
- test('should default to subtle for unknown type', () => {
219
- const base = 's("bd*4")';
220
- const varied = generator.generateVariation(base, 'unknown');
221
- expect(varied).toContain('.sometimes');
222
- });
223
- });
224
- describe('generateFill', () => {
225
- test('should generate techno fill', () => {
226
- const fill = generator.generateFill('techno', 1);
227
- expect(fill).toContain('s(');
228
- expect(fill).toContain('bd');
229
- });
230
- test('should handle different bar lengths', () => {
231
- const fill1 = generator.generateFill('house', 1);
232
- const fill2 = generator.generateFill('house', 2);
233
- expect(fill1).toContain('.fast(1)');
234
- expect(fill2).toContain('.fast(2)');
235
- });
236
- test('should generate fills for all supported styles', () => {
237
- const styles = ['techno', 'house', 'dnb', 'trap', 'breakbeat'];
238
- styles.forEach(style => {
239
- const fill = generator.generateFill(style, 1);
240
- expect(fill).toBeTruthy();
241
- });
242
- });
243
- test('should default to techno for unknown style', () => {
244
- const fill = generator.generateFill('unknown', 1);
245
- expect(fill).toContain('bd');
246
- });
247
- });
248
- describe('generateTransition', () => {
249
- test('should generate transition between styles', () => {
250
- const transition = generator.generateTransition('techno', 'house', 4);
251
- expect(transition).toContain('// Transition from techno to house');
252
- expect(transition).toContain('stack(');
253
- expect(transition).toContain('// Fade out techno');
254
- expect(transition).toContain('// Fade in house');
255
- });
256
- test('should use specified bar length', () => {
257
- const transition8 = generator.generateTransition('house', 'dnb', 8);
258
- expect(transition8).toContain('.slow(8)');
259
- });
260
- });
261
- describe('generateEuclideanPattern', () => {
262
- test('should generate Euclidean pattern with default sound', () => {
263
- const pattern = generator.generateEuclideanPattern(3, 8);
264
- expect(pattern).toContain('s("bd")');
265
- expect(pattern).toContain('.struct(');
266
- });
267
- test('should use specified sound', () => {
268
- const pattern = generator.generateEuclideanPattern(5, 16, 'cp');
269
- expect(pattern).toContain('s("cp")');
270
- });
271
- test('should generate valid rhythm structure', () => {
272
- const pattern = generator.generateEuclideanPattern(4, 12, 'hh');
273
- expect(pattern).toContain('struct');
274
- // Pattern should contain the rhythm from MusicTheory
275
- expect(pattern).toMatch(/1.*~/);
276
- });
277
- });
278
- describe('generatePolyrhythm', () => {
279
- test('should generate polyrhythm with multiple sounds', () => {
280
- const sounds = ['bd', 'cp', 'hh'];
281
- const patterns = [3, 5, 7];
282
- const poly = generator.generatePolyrhythm(sounds, patterns);
283
- expect(poly).toContain('stack(');
284
- expect(poly).toContain('s("bd")');
285
- expect(poly).toContain('s("cp")');
286
- expect(poly).toContain('s("hh")');
287
- expect(poly).toContain('.euclid(3, 16)');
288
- expect(poly).toContain('.euclid(5, 16)');
289
- expect(poly).toContain('.euclid(7, 16)');
290
- });
291
- test('should throw error for mismatched arrays', () => {
292
- const sounds = ['bd', 'cp'];
293
- const patterns = [3, 5, 7];
294
- expect(() => generator.generatePolyrhythm(sounds, patterns))
295
- .toThrow('Number of sounds must match number of patterns');
296
- });
297
- test('should handle single sound/pattern pair', () => {
298
- const poly = generator.generatePolyrhythm(['bd'], [4]);
299
- expect(poly).toContain('s("bd")');
300
- expect(poly).toContain('.euclid(4, 16)');
301
- });
302
- test('should handle empty arrays', () => {
303
- expect(() => generator.generatePolyrhythm([], []))
304
- .not.toThrow();
305
- });
306
- });
307
- describe('integration tests', () => {
308
- test('should create complete musical composition', () => {
309
- const pattern = generator.generateCompletePattern('house', 'Am', 125);
310
- const variation = generator.generateVariation(pattern, 'moderate');
311
- expect(pattern).toBeTruthy();
312
- expect(variation).toContain(pattern);
313
- });
314
- test('should combine multiple generators', () => {
315
- const drums = generator.generateDrumPattern('techno', 0.7);
316
- const bass = generator.generateBassline('C', 'techno');
317
- const fill = generator.generateFill('techno', 1);
318
- expect(drums).toBeTruthy();
319
- expect(bass).toBeTruthy();
320
- expect(fill).toBeTruthy();
321
- });
322
- test('should handle complex workflow', () => {
323
- // Create initial pattern
324
- const initial = generator.generateCompletePattern('dnb', 'D', 174);
325
- expect(initial).toBeTruthy();
326
- // Add variation
327
- const varied = generator.generateVariation(initial, 'glitch');
328
- expect(varied).toContain(initial);
329
- // Generate transition
330
- const transition = generator.generateTransition('dnb', 'ambient', 8);
331
- expect(transition).toBeTruthy();
332
- // Generate final pattern
333
- const final = generator.generateCompletePattern('ambient', 'D', 80);
334
- expect(final).toBeTruthy();
335
- });
336
- });
337
- });