@@ -2,23 +2,25 @@ import * as Tone from 'tone';
2
2
3
3
class ComplementaryToneGenerator {
4
4
constructor ( ) {
5
- this . rootNote = 'C' ;
6
- this . currentOctave = 4 ; // Starting octave
7
- this . octaveDirection = 1 ; // 1 for up, -1 for down
8
- this . octaveMin = 3 ; // Minimum octave limit
9
- this . octaveMax = 5 ; // Maximum octave limit
5
+ // Array of root notes designed to be melodic
6
+
7
+ this . rootNotes = [ 'C' , 'E' , 'G' , 'B' , 'D' , 'F' , 'A' ] ;
8
+ this . currentRootNoteIndex = 0 ; // Start with the first note in the array
9
+ this . rootNote = this . rootNotes [ this . currentRootNoteIndex ] ;
10
+ this . currentOctave = 4 ;
10
11
this . scalePatterns = {
11
- major : [ 2 , 2 , 1 , 2 , 2 , 2 , 1 ] , // W-W-H-W-W-W-H
12
- minor : [ 2 , 1 , 2 , 2 , 1 , 2 , 2 ] , // W-H-W-W-H-W-W
13
- majorSeventh : [ 4 , 3 , 1 ] , // Major 7th Chord (Root-Major 3rd-Perfect 5th-Major 7th)
14
- pentatonic : [ 2 , 2 , 3 , 2 , 3 ] , // Pentatonic Scale
12
+ major : [ 2 , 2 , 1 , 2 , 2 , 2 , 1 ] ,
13
+ minor : [ 2 , 1 , 2 , 2 , 1 , 2 , 2 ] ,
14
+ majorSeventh : [ 4 , 3 , 1 ] ,
15
+ pentatonic : [ 2 , 2 , 3 , 2 , 3 ] ,
15
16
} ;
16
- this . scaleKeys = Object . keys ( this . scalePatterns ) ;
17
- this . currentScalePatternIndex = 0 ; // Index to keep track of the current scale pattern
18
- this . currentScalePattern = this . scaleKeys [ this . currentScalePatternIndex ] ;
17
+ this . currentScalePatternIndex = 0 ;
18
+ this . currentScalePattern = Object . keys ( this . scalePatterns ) [ this . currentScalePatternIndex ] ;
19
19
this . currentScale = [ ] ;
20
- this . scaleIndex = 0 ;
21
- this . lastFrequency = null ;
20
+ this . scaleIndex = - 1 ; // Start before the first note to play the first note immediately
21
+ this . direction = 1 ; // Start ascending
22
+ this . notesSinceRootChange = 0
23
+ this . randomRootChange = 0
22
24
23
25
this . synth = new Tone . Synth ( {
24
26
envelope : {
@@ -32,65 +34,62 @@ class ComplementaryToneGenerator {
32
34
this . initializeScale ( ) ;
33
35
}
34
36
35
- generateScale ( rootNote , pattern ) {
36
- let scale = [ `${ rootNote } ${ this . currentOctave } ` ] ;
37
- let currentFrequency = Tone . Frequency ( scale [ 0 ] ) . toFrequency ( ) ;
38
-
39
- pattern . forEach ( interval => {
40
- currentFrequency *= Tone . intervalToFrequencyRatio ( interval ) ;
41
- scale . push ( Tone . Frequency ( currentFrequency ) . toNote ( ) ) ;
42
- } ) ;
37
+ generateScale ( ) {
38
+ const pattern = this . scalePatterns [ this . currentScalePattern ] ;
39
+ let scale = [ ] ;
40
+ let currentNote = `${ this . rootNote } ${ this . currentOctave } ` ;
41
+ scale . push ( currentNote ) ;
43
42
44
- return scale ;
45
- }
43
+ for ( let i = 0 ; i < pattern . length ; i ++ ) {
44
+ currentNote = Tone . Frequency ( currentNote ) . transpose ( pattern [ i ] ) . toNote ( ) ;
45
+ scale . push ( currentNote ) ;
46
+ }
47
+
48
+ this . currentScale = scale ;
49
+ }
46
50
47
51
initializeScale ( ) {
48
- this . currentScale = this . generateScale ( this . rootNote , this . scalePatterns [ this . currentScalePattern ] ) ;
49
- this . scaleIndex = 0 ; // Reset scale index
52
+ this . reset ( )
53
+ this . generateScale ( ) ;
54
+ this . scaleIndex = 0 ; // Start from the first note
50
55
}
51
56
52
57
playNextNote ( ) {
53
- // Check if we've reached the end or start of the scale to change direction
54
- if ( this . scaleIndex >= this . currentScale . length - 1 || this . scaleIndex <= 0 ) {
55
- this . adjustOctaveAndMaybeScale ( ) ;
58
+ this . notesSinceRootChange ++
59
+ if ( this . notesSinceRootChange > this . randomRootChange ) {
60
+ this . selectNextScaleAndRoot ( )
61
+ }
62
+ if ( this . direction === 1 && this . scaleIndex >= this . currentScale . length - 1 || this . direction === - 1 && this . scaleIndex <= 0 ) {
63
+ this . direction *= - 1 ; // Change direction
64
+ } else {
65
+ this . scaleIndex += this . direction ;
66
+ }
67
+
68
+ if ( this . scaleIndex < 0 || this . scaleIndex >= this . currentScale . length ) { // Ensures index stays within bounds
69
+ return ;
56
70
}
57
71
58
72
const nextNote = this . currentScale [ this . scaleIndex ] ;
59
- this . synth . triggerAttackRelease ( nextNote , '8n' , Tone . now ( ) ) ;
60
- this . scaleIndex += this . octaveDirection ; // Move index in the current direction
61
- this . lastFrequency = Tone . Frequency ( nextNote ) . toFrequency ( ) ;
73
+
74
+ console . log ( nextNote , this . currentRootNoteIndex , this . currentRootNoteIndex )
75
+ this . synth . triggerAttackRelease ( nextNote , '8n' ) ;
62
76
}
63
77
64
- adjustOctaveAndMaybeScale ( ) {
65
- // When changing direction at octave limits, also change the scale
66
- if ( this . currentOctave >= this . octaveMax || this . currentOctave <= this . octaveMin ) {
67
- this . octaveDirection *= - 1 ; // Change direction
68
- this . selectNextScaleAndRoot ( ) ; // Change scale and root note
69
- } else if ( this . scaleIndex === 0 || this . scaleIndex === this . currentScale . length - 1 ) {
70
- // Regular octave direction change without hitting octave limits
71
- this . octaveDirection *= - 1 ; // Change direction
72
- }
78
+ selectNextScaleAndRoot ( ) {
79
+ this . reset ( )
80
+ this . currentRootNoteIndex = ( this . currentRootNoteIndex + 1 ) % this . rootNotes . length ;
81
+ this . rootNote = this . rootNotes [ this . currentRootNoteIndex ] ;
73
82
74
- // Adjust the octave within the limits
75
- this . currentOctave += this . octaveDirection ;
76
- if ( this . currentOctave > this . octaveMax ) {
77
- this . currentOctave = this . octaveMax ;
78
- } else if ( this . currentOctave < this . octaveMin ) {
79
- this . currentOctave = this . octaveMin ;
80
- }
83
+ const max = Object . keys ( this . scalePatterns ) . length
84
+ this . currentScalePatternIndex = Math . floor ( Math . random ( ) * ( max - 0 + 1 ) ) + 0 ;
85
+ this . currentScalePattern = Object . keys ( this . scalePatterns ) [ this . currentScalePatternIndex ] ;
81
86
82
- this . initializeScale ( ) ; // Re-initialize the scale with the new settings
87
+ this . initializeScale ( ) ;
83
88
}
84
89
85
- selectNextScaleAndRoot ( ) {
86
- // Move to the next scale pattern
87
- this . currentScalePatternIndex = ( this . currentScalePatternIndex + 1 ) % this . scaleKeys . length ;
88
- this . currentScalePattern = this . scaleKeys [ this . currentScalePatternIndex ] ;
89
-
90
- const rootNotes = [ 'C' , 'D' , 'E' , 'B' , 'G' , 'F' , 'A' ] ;
91
- let currentRootIndex = rootNotes . indexOf ( this . rootNote ) ;
92
- currentRootIndex = ( currentRootIndex + 1 ) % rootNotes . length ;
93
- this . rootNote = rootNotes [ currentRootIndex ] ;
90
+ reset ( ) {
91
+ this . randomRootChange = Math . floor ( Math . random ( ) * ( 12 - 3 + 1 ) ) + 3
92
+ this . notesSinceRootChange = 0
94
93
}
95
94
}
96
95
0 commit comments