@@ -7,74 +7,191 @@ const AudioNodeMixin = require('./AudioNode.mixin.js');
7
7
$ { d . parent ( d . node ) === 'AudioScheduledSourceNode' ?
8
8
`const AudioScheduledSourceNodeMixin = require('./AudioScheduledSourceNode.mixin.js');` : `` }
9
9
10
+ const { kNativeAudioBuffer, kAudioBuffer } = require ( './AudioBuffer.js' ) ;
11
+
10
12
module . exports = ( Native$ { d . name ( d . node ) } ) => {
11
- $ { d . parent ( d . node ) === 'AudioScheduledSourceNode' ? `
12
13
const EventTarget = EventTargetMixin ( Native$ { d . name ( d . node ) } , [ 'ended' ] ) ;
13
14
const AudioNode = AudioNodeMixin ( EventTarget ) ;
15
+ $ { d . parent ( d . node ) === 'AudioScheduledSourceNode' ? `\
14
16
const AudioScheduledSourceNode = AudioScheduledSourceNodeMixin(AudioNode);
15
17
16
- class ${ d . name ( d . node ) } extends AudioScheduledSourceNode {
18
+ class ${ d . name ( d . node ) } extends AudioScheduledSourceNode {` : `
19
+ class ${ d . name ( d . node ) } extends AudioNode {`
20
+ }
17
21
constructor ( context , options ) {
18
- if (options !== undefined && typeof options !== 'object') {
19
- throw new TypeError("Failed to construct '${ d . name ( d . node ) } ': argument 2 is not of type '${ d . name ( d . node ) . replace ( "Node" , "Options" ) } '")
22
+ // keep a handle to the original object, if we need to manipulate the
23
+ // options before passing them to NAPI
24
+ const originalOptions = Object . assign ( { } , options ) ;
25
+
26
+ $ { ( function ( ) {
27
+ // handle argument 2: options
28
+ const options = d . constructor ( d . node ) . arguments [ 1 ] ;
29
+ const optionsType = d . memberType ( options ) ;
30
+ const optionsIdl = d . findInTree ( optionsType ) ;
31
+ let checkOptions = `
32
+ if (options !== undefined) {
33
+ if (typeof options !== 'object') {
34
+ throw new TypeError("Failed to construct '${ d . name ( d . node ) } ': argument 2 is not of type '${ optionsType } '")
35
+ }
36
+ ` ;
37
+
38
+ checkOptions += optionsIdl . members . map ( member => {
39
+ // @todo - improve checks
40
+ // cf. https://github.com/jsdom/webidl-conversions
41
+ const optionName = d . name ( member ) ;
42
+ const type = d . memberType ( member ) ;
43
+ const required = member . required ;
44
+ const nullable = member . idlType . nullable ;
45
+ const defaultValue = member . default ; // null or object
46
+ let checkMember = '' ;
47
+
48
+ if ( required ) {
49
+ checkMember += `
50
+ if (options && !('${ optionName } ' in options)) {
51
+ throw new Error("Failed to read the '${ optionName } '' property from ${ optionsType } : Required member is undefined.")
52
+ }
53
+ `
54
+ }
55
+
56
+ // d.debug(member);
57
+ switch ( type ) {
58
+ case 'AudioBuffer' : {
59
+ checkMember += `
60
+ if ('${ optionName } ' in options) {
61
+ if (options.${ optionName } !== null) {
62
+ if (!(kNativeAudioBuffer in options.${ optionName } )) {
63
+ throw new TypeError("Failed to set the 'buffer' property on 'AudioBufferSourceNode': Failed to convert value to 'AudioBuffer'");
64
+ }
65
+
66
+ // unwrap napi audio buffer, clone the options object as it might be reused
67
+ options = Object.assign({}, options);
68
+ options.${ optionName } = options.${ optionName } [kNativeAudioBuffer];
69
+ }
70
+ }
71
+ ` ;
72
+ break ;
73
+ }
74
+ }
75
+
76
+ return checkMember ;
77
+ } ) . join ( '' ) ;
78
+
79
+ checkOptions += `
20
80
}
81
+ ` ;
82
+
83
+ return checkOptions ;
84
+ } ( ) ) }
21
85
22
86
super ( context , options ) ;
23
- // EventTargetMixin has been called so EventTargetMixin[kDispatchEvent] is
24
- // bound to this, then we can safely finalize event target initialization
25
- super.__initEventTarget__();
26
- ${ d . audioParams ( d . node ) . map ( param => {
27
- return `
28
- this.${ d . name ( param ) } = new AudioParam(this.${ d . name ( param ) } );` ;
29
- } ) . join ( '' ) }
30
- }
31
- ` : `
32
- const EventTarget = EventTargetMixin(Native${ d . name ( d . node ) } );
33
- const AudioNode = AudioNodeMixin(EventTarget);
34
87
35
- class ${ d . name ( d . node ) } extends AudioNode {
36
- constructor(context, options) {
37
- if (options !== undefined && typeof options !== 'object') {
38
- throw new TypeError("Failed to construct '${ d . name ( d . node ) } ': argument 2 is not of type '${ d . name ( d . node ) . replace ( "Node" , "Options" ) } '")
88
+ $ { ( function ( ) {
89
+ // handle special options cases
90
+ const options = d . constructor ( d . node ) . arguments [ 1 ] ;
91
+ const optionsType = d . memberType ( options ) ;
92
+ const optionsIdl = d . findInTree ( optionsType ) ;
93
+
94
+ return optionsIdl . members . map ( member => {
95
+ // at this point all type checks have been done, so it is safe to just manipulate the options
96
+ const optionName = d . name ( member ) ;
97
+ const type = d . memberType ( member ) ;
98
+ if ( type === 'AudioBuffer' ) {
99
+ return `
100
+ // keep the wrapper AudioBuffer wrapperaround
101
+ this[kAudioBuffer] = null;
102
+
103
+ if (options && '${ optionName } ' in options) {
104
+ this[kAudioBuffer] = originalOptions.${ optionName } ;
39
105
}
106
+ ` ;
107
+ }
108
+ } ) . join ( '' ) ;
109
+ } ( ) ) }
40
110
41
- super(context, options);
42
- ${ d . audioParams ( d . node ) . map ( param => {
43
- return `
111
+ $ { d . parent ( d . node ) === 'AudioScheduledSourceNode' ? `
112
+ // EventTargetMixin constructor has been called so EventTargetMixin[kDispatchEvent]
113
+ // is bound to this, then we can safely finalize event target initialization
114
+ super.__initEventTarget__();` : `` }
115
+
116
+ $ { d . audioParams ( d . node ) . map ( param => {
117
+ return `
44
118
this.${ d . name ( param ) } = new AudioParam(this.${ d . name ( param ) } );` ;
45
- } ) . join ( '' ) }
119
+ } ) . join ( '' ) }
46
120
}
47
- ` }
121
+
48
122
// getters
49
123
${d . attributes ( d . node ) . map ( attr => {
50
- return `
124
+ switch ( d . memberType ( attr ) ) {
125
+ case 'AudioBuffer' : {
126
+ return `
127
+ get ${ d . name ( attr ) } () {
128
+ return this[kAudioBuffer];
129
+ }
130
+ ` ;
131
+ break ;
132
+ }
133
+ default : {
134
+ return `
51
135
get ${ d . name ( attr ) } () {
52
136
return super.${ d . name ( attr ) } ;
53
137
}
54
- ` } ) . join ( '' ) }
138
+ ` ;
139
+ break ;
140
+ }
141
+ }
142
+ } ) . join ( '' ) }
55
143
// setters
56
144
${d . attributes ( d . node ) . filter ( attr => ! attr . readonly ) . map ( attr => {
57
- return `
145
+ switch ( d . memberType ( attr ) ) {
146
+ case 'AudioBuffer' : {
147
+ return `
148
+ // @todo - should be able to set to null afterward
149
+ set ${ d . name ( attr ) } (value) {
150
+ if (value === null) {
151
+ return;
152
+ } else if (!(kNativeAudioBuffer in value)) {
153
+ throw new TypeError("Failed to set the 'buffer' property on 'AudioBufferSourceNode': Failed to convert value to 'AudioBuffer'");
154
+ }
155
+
156
+ try {
157
+ super.${ d . name ( attr ) } = value[kNativeAudioBuffer];
158
+ } catch (err) {
159
+ throwSanitizedError(err);
160
+ }
161
+
162
+ this[kAudioBuffer] = value;
163
+ }
164
+ ` ;
165
+ break ;
166
+ }
167
+ default : {
168
+ return `
58
169
set ${ d . name ( attr ) } (value) {
59
170
try {
60
171
super.${ d . name ( attr ) } = value;
61
172
} catch (err) {
62
173
throwSanitizedError(err);
63
174
}
64
175
}
65
- ` } ) . join ( '' ) }
176
+ ` ;
177
+ break ;
178
+ }
179
+ }
180
+ } ) . join ( '' ) }
181
+
66
182
// methods
67
- ${d . methods ( d . node , false ) . reduce ( ( acc , method ) => {
68
- // dedup method names
69
- if ( ! acc . find ( i => d . name ( i ) === d . name ( method ) ) ) {
70
- acc . push ( method )
71
- }
72
- return acc ;
73
- } , [ ] )
74
- // filter AudioScheduledSourceNode methods to prevent re-throwing errors
75
- . filter ( method => d . name ( method ) !== 'start' && d . name ( method ) !== 'stop' )
76
- . map ( method => {
77
- return `
183
+ ${d . methods ( d . node , false )
184
+ . reduce ( ( acc , method ) => {
185
+ // dedup method names
186
+ if ( ! acc . find ( i => d . name ( i ) === d . name ( method ) ) ) {
187
+ acc . push ( method )
188
+ }
189
+ return acc ;
190
+ } , [ ] )
191
+ // filter AudioScheduledSourceNode methods to prevent re-throwing errors
192
+ . filter ( method => d . name ( method ) !== 'start' && d . name ( method ) !== 'stop' )
193
+ . map ( method => {
194
+ return `
78
195
${ d . name ( method ) } (...args) {
79
196
try {
80
197
return super.${ d . name ( method ) } (...args);
0 commit comments