@@ -17,29 +17,24 @@ class PitchDetection {
17
17
this . model = model ;
18
18
this . audioContext = audioContext ;
19
19
this . stream = stream ;
20
+ this . frequency = null ;
20
21
this . ready = callCallback ( this . loadModel ( model ) , callback ) ;
21
22
}
22
23
23
24
async loadModel ( model ) {
24
25
this . model = await tf . loadModel ( `${ model } /model.json` ) ;
25
- await this . initAudio ( ) ;
26
- return this ;
27
- }
28
-
29
- initAudio ( ) {
30
26
if ( this . audioContext ) {
31
- try {
32
- this . processStream ( this . stream ) ;
33
- } catch ( e ) {
34
- throw new Error ( `Error: Could not access microphone - ${ e } ` ) ;
35
- }
27
+ await this . processStream ( ) ;
36
28
} else {
37
29
throw new Error ( 'Could not access microphone - getUserMedia not available' ) ;
38
30
}
31
+ return this ;
39
32
}
40
33
41
- processStream ( stream ) {
42
- const mic = this . audioContext . createMediaStreamSource ( stream ) ;
34
+ async processStream ( ) {
35
+ await tf . nextFrame ( ) ;
36
+
37
+ const mic = this . audioContext . createMediaStreamSource ( this . stream ) ;
43
38
const minBufferSize = ( this . audioContext . sampleRate / 16000 ) * 1024 ;
44
39
let bufferSize = 4 ;
45
40
while ( bufferSize < minBufferSize ) bufferSize *= 2 ;
@@ -58,25 +53,8 @@ class PitchDetection {
58
53
}
59
54
}
60
55
61
- static resample ( audioBuffer , onComplete ) {
62
- const interpolate = ( audioBuffer . sampleRate % 16000 !== 0 ) ;
63
- const multiplier = audioBuffer . sampleRate / 16000 ;
64
- const original = audioBuffer . getChannelData ( 0 ) ;
65
- const subsamples = new Float32Array ( 1024 ) ;
66
- for ( let i = 0 ; i < 1024 ; i += 1 ) {
67
- if ( ! interpolate ) {
68
- subsamples [ i ] = original [ i * multiplier ] ;
69
- } else {
70
- const left = Math . floor ( i * multiplier ) ;
71
- const right = left + 1 ;
72
- const p = ( i * multiplier ) - left ;
73
- subsamples [ i ] = ( ( ( 1 - p ) * original [ left ] ) + ( p * original [ right ] ) ) ;
74
- }
75
- }
76
- onComplete ( subsamples ) ;
77
- }
78
-
79
- processMicrophoneBuffer ( event ) {
56
+ async processMicrophoneBuffer ( event ) {
57
+ await tf . nextFrame ( ) ;
80
58
this . results = { } ;
81
59
const centMapping = tf . add ( tf . linspace ( 0 , 7180 , 360 ) , tf . tensor ( 1997.3794084376191 ) ) ;
82
60
PitchDetection . resample ( event . inputBuffer , ( resampled ) => {
@@ -103,14 +81,38 @@ class PitchDetection {
103
81
const predictedCent = productSum / weightSum ;
104
82
const predictedHz = 10 * ( ( predictedCent / 1200.0 ) ** 2 ) ;
105
83
106
- const result = ( confidence > 0.5 ) ? ` ${ predictedHz . toFixed ( 3 ) } + Hz` : 'no voice' ;
107
- this . results . result = result ;
84
+ const frequency = ( confidence > 0.5 ) ? predictedHz : null ;
85
+ this . frequency = frequency ;
108
86
} ) ;
109
87
} ) ;
110
88
}
111
89
112
- getResults ( ) {
113
- return this . results ;
90
+ async getPitch ( callback ) {
91
+ await this . ready ;
92
+ await tf . nextFrame ( ) ;
93
+ const { frequency } = this ;
94
+ if ( callback ) {
95
+ callback ( undefined , frequency ) ;
96
+ }
97
+ return frequency ;
98
+ }
99
+
100
+ static resample ( audioBuffer , onComplete ) {
101
+ const interpolate = ( audioBuffer . sampleRate % 16000 !== 0 ) ;
102
+ const multiplier = audioBuffer . sampleRate / 16000 ;
103
+ const original = audioBuffer . getChannelData ( 0 ) ;
104
+ const subsamples = new Float32Array ( 1024 ) ;
105
+ for ( let i = 0 ; i < 1024 ; i += 1 ) {
106
+ if ( ! interpolate ) {
107
+ subsamples [ i ] = original [ i * multiplier ] ;
108
+ } else {
109
+ const left = Math . floor ( i * multiplier ) ;
110
+ const right = left + 1 ;
111
+ const p = ( i * multiplier ) - left ;
112
+ subsamples [ i ] = ( ( ( 1 - p ) * original [ left ] ) + ( p * original [ right ] ) ) ;
113
+ }
114
+ }
115
+ onComplete ( subsamples ) ;
114
116
}
115
117
}
116
118
0 commit comments