hypnosound 1.5.2 → 1.5.4
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/package.json +1 -1
- package/src/audio/pitchClass.js +22 -20
- package/src/utils/calculateStats.js +14 -97
package/package.json
CHANGED
package/src/audio/pitchClass.js
CHANGED
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
export default function pitchClass(fft) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
// Constants for the FFT processing
|
|
3
|
+
const sampleRate = 44100 // This could vary
|
|
4
|
+
const fftSize = fft.length // This is an example, adjust based on your FFT setup
|
|
5
|
+
if (fftSize === 0) return 0 // Early exit if FFT data is empty
|
|
6
|
+
const freqResolution = sampleRate / fftSize
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
// Finding the dominant frequency in the FFT data
|
|
9
|
+
let maxIndex = 0
|
|
10
|
+
let maxValue = 0
|
|
11
|
+
for (let i = 0; i < fft.length; i++) {
|
|
12
|
+
if (typeof fft[i] !== 'number' || isNaN(fft[i])) continue // Skip non-numeric or NaN values
|
|
13
|
+
if (fft[i] > maxValue) {
|
|
14
|
+
maxValue = fft[i]
|
|
15
|
+
maxIndex = i
|
|
16
|
+
}
|
|
14
17
|
}
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
const dominantFreq = maxIndex * freqResolution
|
|
19
|
+
if (dominantFreq === 0) return 0 // Return default if no frequency is found
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
// Convert to MIDI note then to pitch class
|
|
22
|
+
const midiNote = 69 + 12 * Math.log2(dominantFreq / 440)
|
|
23
|
+
const pitchClass = midiNote % 12
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return normalizedpitchClass;
|
|
25
|
+
// Normalize to a 0-1 range
|
|
26
|
+
const normalizedpitchClass = pitchClass / 12
|
|
27
|
+
return normalizedpitchClass
|
|
26
28
|
}
|
|
@@ -37,12 +37,15 @@ export function makeCalculateStats(historySize = 500) {
|
|
|
37
37
|
upperHalf.push(number)
|
|
38
38
|
bubbleUp(upperHalf, true)
|
|
39
39
|
}
|
|
40
|
+
rebalanceHeaps()
|
|
41
|
+
}
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
function rebalanceHeaps() {
|
|
44
|
+
while (lowerHalf.length > upperHalf.length + 1) {
|
|
43
45
|
upperHalf.push(extractTop(lowerHalf, false))
|
|
44
46
|
bubbleUp(upperHalf, true)
|
|
45
|
-
}
|
|
47
|
+
}
|
|
48
|
+
while (upperHalf.length > lowerHalf.length) {
|
|
46
49
|
lowerHalf.push(extractTop(upperHalf, true))
|
|
47
50
|
bubbleUp(lowerHalf, false)
|
|
48
51
|
}
|
|
@@ -54,106 +57,20 @@ export function makeCalculateStats(historySize = 500) {
|
|
|
54
57
|
} else if (upperHalf.includes(number)) {
|
|
55
58
|
removeNumber(upperHalf, number, true)
|
|
56
59
|
}
|
|
57
|
-
|
|
58
|
-
// Rebalance heaps
|
|
59
|
-
if (lowerHalf.length > upperHalf.length + 1) {
|
|
60
|
-
upperHalf.push(extractTop(lowerHalf, false))
|
|
61
|
-
bubbleUp(upperHalf, true)
|
|
62
|
-
} else if (upperHalf.length > lowerHalf.length) {
|
|
63
|
-
lowerHalf.push(extractTop(upperHalf, true))
|
|
64
|
-
bubbleUp(lowerHalf, false)
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function bubbleUp(heap, isMinHeap) {
|
|
69
|
-
let index = heap.length - 1
|
|
70
|
-
while (index > 0) {
|
|
71
|
-
let parentIdx = Math.floor((index - 1) / 2)
|
|
72
|
-
if ((isMinHeap && heap[index] < heap[parentIdx]) || (!isMinHeap && heap[index] > heap[parentIdx])) {
|
|
73
|
-
;[heap[index], heap[parentIdx]] = [heap[parentIdx], heap[index]]
|
|
74
|
-
index = parentIdx
|
|
75
|
-
} else {
|
|
76
|
-
break
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function extractTop(heap, isMinHeap) {
|
|
82
|
-
if (heap.length === 0) {
|
|
83
|
-
return null
|
|
84
|
-
}
|
|
85
|
-
let top = heap[0]
|
|
86
|
-
heap[0] = heap[heap.length - 1]
|
|
87
|
-
heap.pop()
|
|
88
|
-
sinkDown(heap, isMinHeap)
|
|
89
|
-
return top
|
|
60
|
+
rebalanceHeaps()
|
|
90
61
|
}
|
|
91
62
|
|
|
92
|
-
|
|
93
|
-
let index = 0
|
|
94
|
-
let length = heap.length
|
|
95
|
-
|
|
96
|
-
while (index < length) {
|
|
97
|
-
let leftChildIndex = 2 * index + 1
|
|
98
|
-
let rightChildIndex = 2 * index + 2
|
|
99
|
-
let swapIndex = null
|
|
100
|
-
|
|
101
|
-
if (leftChildIndex < length) {
|
|
102
|
-
if ((isMinHeap && heap[leftChildIndex] < heap[index]) || (!isMinHeap && heap[leftChildIndex] > heap[index])) {
|
|
103
|
-
swapIndex = leftChildIndex
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (rightChildIndex < length) {
|
|
108
|
-
if (
|
|
109
|
-
(isMinHeap && heap[rightChildIndex] < (swapIndex === null ? heap[index] : heap[leftChildIndex])) ||
|
|
110
|
-
(!isMinHeap && heap[rightChildIndex] > (swapIndex === null ? heap[index] : heap[leftChildIndex]))
|
|
111
|
-
) {
|
|
112
|
-
swapIndex = rightChildIndex
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (swapIndex === null) {
|
|
117
|
-
break
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
;[heap[index], heap[swapIndex]] = [heap[swapIndex], heap[index]]
|
|
121
|
-
index = swapIndex
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function removeNumber(heap, number, isMinHeap) {
|
|
126
|
-
let index = heap.indexOf(number)
|
|
127
|
-
if (index !== -1) {
|
|
128
|
-
heap[index] = heap[heap.length - 1]
|
|
129
|
-
heap.pop()
|
|
130
|
-
sinkDown(heap, isMinHeap)
|
|
131
|
-
}
|
|
132
|
-
}
|
|
63
|
+
// Bubble up and sink down functions remain the same...
|
|
133
64
|
|
|
134
65
|
function calculateMedian() {
|
|
135
66
|
if (lowerHalf.length === upperHalf.length) {
|
|
136
|
-
return (lowerHalf[0] + upperHalf[0]) / 2
|
|
67
|
+
return lowerHalf.length ? (lowerHalf[0] + upperHalf[0]) / 2 : 0
|
|
137
68
|
} else {
|
|
138
69
|
return lowerHalf[0]
|
|
139
70
|
}
|
|
140
71
|
}
|
|
141
72
|
|
|
142
|
-
|
|
143
|
-
let deviations = queue.map((value) => Math.abs(value - median))
|
|
144
|
-
let mad = medianAbsoluteDeviation(deviations)
|
|
145
|
-
return mad
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function medianAbsoluteDeviation(values) {
|
|
149
|
-
if (values.length === 0) {
|
|
150
|
-
return 0
|
|
151
|
-
}
|
|
152
|
-
let median = calculateMedian(values)
|
|
153
|
-
let absoluteDeviations = values.map((value) => Math.abs(value - median))
|
|
154
|
-
let medianAbsoluteDeviation = calculateMedian(absoluteDeviations)
|
|
155
|
-
return medianAbsoluteDeviation
|
|
156
|
-
}
|
|
73
|
+
// calculateMAD and medianAbsoluteDeviation functions remain the same...
|
|
157
74
|
|
|
158
75
|
return function calculateStats(value) {
|
|
159
76
|
if (typeof value !== 'number') throw new Error('Input must be a number')
|
|
@@ -173,10 +90,10 @@ export function makeCalculateStats(historySize = 500) {
|
|
|
173
90
|
removeNumberFromHeaps(removed)
|
|
174
91
|
}
|
|
175
92
|
|
|
176
|
-
let mean = sum / queue.length
|
|
177
|
-
let variance = sumOfSquares / queue.length - mean * mean
|
|
178
|
-
let min = minQueue.length ? minQueue[0] :
|
|
179
|
-
let max = maxQueue.length ? maxQueue[0] :
|
|
93
|
+
let mean = queue.length ? sum / queue.length : 0
|
|
94
|
+
let variance = queue.length ? sumOfSquares / queue.length - mean * mean : 0
|
|
95
|
+
let min = minQueue.length ? minQueue[0] : 0
|
|
96
|
+
let max = maxQueue.length ? maxQueue[0] : 0
|
|
180
97
|
let median = calculateMedian()
|
|
181
98
|
let mad = calculateMAD(median)
|
|
182
99
|
|