|
1 | 1 | // inspiration: recorder.js, Tone.js & typedarray.org
|
2 | 2 |
|
3 | 3 | import p5sound from './master';
|
4 |
| -import { convertToWav, safeBufferSize } from './helpers'; |
| 4 | +import { safeBufferSize } from './helpers'; |
5 | 5 | import processorNames from './audioWorklet/processorNames';
|
6 | 6 |
|
7 | 7 | const ac = p5sound.audiocontext;
|
@@ -80,145 +80,132 @@ const ac = p5sound.audiocontext;
|
80 | 80 | * }
|
81 | 81 | * </div></code>
|
82 | 82 | */
|
83 |
| -p5.SoundRecorder = function () { |
84 |
| - this.input = ac.createGain(); |
85 |
| - this.output = ac.createGain(); |
86 |
| - |
87 |
| - this._inputChannels = 2; |
88 |
| - this._outputChannels = 2; // stereo output, even if input is mono |
89 |
| - |
90 |
| - const workletBufferSize = safeBufferSize(1024); |
91 |
| - |
92 |
| - this._workletNode = new AudioWorkletNode( |
93 |
| - ac, |
94 |
| - processorNames.recorderProcessor, |
95 |
| - { |
96 |
| - outputChannelCount: [this._outputChannels], |
97 |
| - processorOptions: { |
98 |
| - numInputChannels: this._inputChannels, |
99 |
| - bufferSize: workletBufferSize, |
100 |
| - }, |
101 |
| - } |
102 |
| - ); |
103 |
| - |
104 |
| - this._workletNode.port.onmessage = function (event) { |
105 |
| - if (event.data.name === 'buffers') { |
106 |
| - const buffers = [ |
107 |
| - new Float32Array(event.data.leftBuffer), |
108 |
| - new Float32Array(event.data.rightBuffer), |
109 |
| - ]; |
110 |
| - this._callback(buffers); |
111 |
| - } |
112 |
| - }.bind(this); |
| 83 | +class SoundRecorder { |
| 84 | + constructor() { |
| 85 | + this.input = ac.createGain(); |
| 86 | + this.output = ac.createGain(); |
| 87 | + |
| 88 | + this._inputChannels = 2; |
| 89 | + this._outputChannels = 2; // stereo output, even if input is mono |
| 90 | + |
| 91 | + const workletBufferSize = safeBufferSize(1024); |
| 92 | + |
| 93 | + this._workletNode = new AudioWorkletNode( |
| 94 | + ac, |
| 95 | + processorNames.recorderProcessor, |
| 96 | + { |
| 97 | + outputChannelCount: [this._outputChannels], |
| 98 | + processorOptions: { |
| 99 | + numInputChannels: this._inputChannels, |
| 100 | + bufferSize: workletBufferSize, |
| 101 | + }, |
| 102 | + } |
| 103 | + ); |
| 104 | + |
| 105 | + this._workletNode.port.onmessage = function (event) { |
| 106 | + if (event.data.name === 'buffers') { |
| 107 | + const buffers = [ |
| 108 | + new Float32Array(event.data.leftBuffer), |
| 109 | + new Float32Array(event.data.rightBuffer), |
| 110 | + ]; |
| 111 | + this._callback(buffers); |
| 112 | + } |
| 113 | + }.bind(this); |
| 114 | + |
| 115 | + /** |
| 116 | + * callback invoked when the recording is over |
| 117 | + * @private |
| 118 | + * @type Function(Float32Array) |
| 119 | + */ |
| 120 | + this._callback = function () {}; |
| 121 | + |
| 122 | + // connections |
| 123 | + this._workletNode.connect(p5.soundOut._silentNode); |
| 124 | + this.setInput(); |
| 125 | + |
| 126 | + // add this p5.SoundFile to the soundArray |
| 127 | + p5sound.soundArray.push(this); |
| 128 | + } |
113 | 129 |
|
114 | 130 | /**
|
115 |
| - * callback invoked when the recording is over |
116 |
| - * @private |
117 |
| - * @type Function(Float32Array) |
| 131 | + * Connect a specific device to the p5.SoundRecorder. |
| 132 | + * If no parameter is given, p5.SoundRecorer will record |
| 133 | + * all audible p5.sound from your sketch. |
| 134 | + * |
| 135 | + * @method setInput |
| 136 | + * @for p5.SoundRecorder |
| 137 | + * @param {Object} [unit] p5.sound object or a web audio unit |
| 138 | + * that outputs sound |
118 | 139 | */
|
119 |
| - this._callback = function () {}; |
120 |
| - |
121 |
| - // connections |
122 |
| - this._workletNode.connect(p5.soundOut._silentNode); |
123 |
| - this.setInput(); |
124 |
| - |
125 |
| - // add this p5.SoundFile to the soundArray |
126 |
| - p5sound.soundArray.push(this); |
127 |
| -}; |
128 |
| - |
129 |
| -/** |
130 |
| - * Connect a specific device to the p5.SoundRecorder. |
131 |
| - * If no parameter is given, p5.SoundRecorer will record |
132 |
| - * all audible p5.sound from your sketch. |
133 |
| - * |
134 |
| - * @method setInput |
135 |
| - * @for p5.SoundRecorder |
136 |
| - * @param {Object} [unit] p5.sound object or a web audio unit |
137 |
| - * that outputs sound |
138 |
| - */ |
139 |
| -p5.SoundRecorder.prototype.setInput = function (unit) { |
140 |
| - this.input.disconnect(); |
141 |
| - this.input = null; |
142 |
| - this.input = ac.createGain(); |
143 |
| - this.input.connect(this._workletNode); |
144 |
| - this.input.connect(this.output); |
145 |
| - if (unit) { |
146 |
| - unit.connect(this.input); |
147 |
| - } else { |
148 |
| - p5.soundOut.output.connect(this.input); |
| 140 | + setInput(unit) { |
| 141 | + this.input.disconnect(); |
| 142 | + this.input = null; |
| 143 | + this.input = ac.createGain(); |
| 144 | + this.input.connect(this._workletNode); |
| 145 | + this.input.connect(this.output); |
| 146 | + if (unit) { |
| 147 | + unit.connect(this.input); |
| 148 | + } else { |
| 149 | + p5.soundOut.output.connect(this.input); |
| 150 | + } |
149 | 151 | }
|
150 |
| -}; |
151 | 152 |
|
152 |
| -/** |
153 |
| - * Start recording. To access the recording, provide |
154 |
| - * a p5.SoundFile as the first parameter. The p5.SoundRecorder |
155 |
| - * will send its recording to that p5.SoundFile for playback once |
156 |
| - * recording is complete. Optional parameters include duration |
157 |
| - * (in seconds) of the recording, and a callback function that |
158 |
| - * will be called once the complete recording has been |
159 |
| - * transfered to the p5.SoundFile. |
160 |
| - * |
161 |
| - * @method record |
162 |
| - * @for p5.SoundRecorder |
163 |
| - * @param {p5.SoundFile} soundFile p5.SoundFile |
164 |
| - * @param {Number} [duration] Time (in seconds) |
165 |
| - * @param {Function} [callback] The name of a function that will be |
166 |
| - * called once the recording completes |
167 |
| - */ |
168 |
| -p5.SoundRecorder.prototype.record = function (sFile, duration, callback) { |
169 |
| - this._workletNode.port.postMessage({ name: 'start', duration: duration }); |
170 |
| - |
171 |
| - if (sFile && callback) { |
172 |
| - this._callback = function (buffer) { |
173 |
| - sFile.setBuffer(buffer); |
174 |
| - callback(); |
175 |
| - }; |
176 |
| - } else if (sFile) { |
177 |
| - this._callback = function (buffer) { |
178 |
| - sFile.setBuffer(buffer); |
179 |
| - }; |
| 153 | + /** |
| 154 | + * Start recording. To access the recording, provide |
| 155 | + * a p5.SoundFile as the first parameter. The p5.SoundRecorder |
| 156 | + * will send its recording to that p5.SoundFile for playback once |
| 157 | + * recording is complete. Optional parameters include duration |
| 158 | + * (in seconds) of the recording, and a callback function that |
| 159 | + * will be called once the complete recording has been |
| 160 | + * transfered to the p5.SoundFile. |
| 161 | + * |
| 162 | + * @method record |
| 163 | + * @for p5.SoundRecorder |
| 164 | + * @param {p5.SoundFile} soundFile p5.SoundFile |
| 165 | + * @param {Number} [duration] Time (in seconds) |
| 166 | + * @param {Function} [callback] The name of a function that will be |
| 167 | + * called once the recording completes |
| 168 | + */ |
| 169 | + record(sFile, duration, callback) { |
| 170 | + this._workletNode.port.postMessage({ name: 'start', duration: duration }); |
| 171 | + |
| 172 | + if (sFile && callback) { |
| 173 | + this._callback = function (buffer) { |
| 174 | + sFile.setBuffer(buffer); |
| 175 | + callback(); |
| 176 | + }; |
| 177 | + } else if (sFile) { |
| 178 | + this._callback = function (buffer) { |
| 179 | + sFile.setBuffer(buffer); |
| 180 | + }; |
| 181 | + } |
180 | 182 | }
|
181 |
| -}; |
182 | 183 |
|
183 |
| -/** |
184 |
| - * Stop the recording. Once the recording is stopped, |
185 |
| - * the results will be sent to the p5.SoundFile that |
186 |
| - * was given on .record(), and if a callback function |
187 |
| - * was provided on record, that function will be called. |
188 |
| - * |
189 |
| - * @method stop |
190 |
| - * @for p5.SoundRecorder |
191 |
| - */ |
192 |
| -p5.SoundRecorder.prototype.stop = function () { |
193 |
| - this._workletNode.port.postMessage({ name: 'stop' }); |
194 |
| -}; |
| 184 | + /** |
| 185 | + * Stop the recording. Once the recording is stopped, |
| 186 | + * the results will be sent to the p5.SoundFile that |
| 187 | + * was given on .record(), and if a callback function |
| 188 | + * was provided on record, that function will be called. |
| 189 | + * |
| 190 | + * @method stop |
| 191 | + * @for p5.SoundRecorder |
| 192 | + */ |
| 193 | + stop() { |
| 194 | + this._workletNode.port.postMessage({ name: 'stop' }); |
| 195 | + } |
195 | 196 |
|
196 |
| -p5.SoundRecorder.prototype.dispose = function () { |
197 |
| - // remove reference from soundArray |
198 |
| - var index = p5sound.soundArray.indexOf(this); |
199 |
| - p5sound.soundArray.splice(index, 1); |
| 197 | + dispose() { |
| 198 | + // remove reference from soundArray |
| 199 | + var index = p5sound.soundArray.indexOf(this); |
| 200 | + p5sound.soundArray.splice(index, 1); |
200 | 201 |
|
201 |
| - this._callback = function () {}; |
202 |
| - if (this.input) { |
203 |
| - this.input.disconnect(); |
| 202 | + this._callback = function () {}; |
| 203 | + if (this.input) { |
| 204 | + this.input.disconnect(); |
| 205 | + } |
| 206 | + this.input = null; |
| 207 | + this._workletNode = null; |
204 | 208 | }
|
205 |
| - this.input = null; |
206 |
| - this._workletNode = null; |
207 |
| -}; |
| 209 | +} |
208 | 210 |
|
209 |
| -/** |
210 |
| - * Save a p5.SoundFile as a .wav file. The browser will prompt the user |
211 |
| - * to download the file to their device. |
212 |
| - * For uploading audio to a server, use |
213 |
| - * <a href="/docs/reference/#/p5.SoundFile/saveBlob">`p5.SoundFile.saveBlob`</a>. |
214 |
| - * |
215 |
| - * @for p5 |
216 |
| - * @method saveSound |
217 |
| - * @param {p5.SoundFile} soundFile p5.SoundFile that you wish to save |
218 |
| - * @param {String} fileName name of the resulting .wav file. |
219 |
| - */ |
220 |
| -// add to p5.prototype as this is used by the p5 `save()` method. |
221 |
| -p5.prototype.saveSound = function (soundFile, fileName) { |
222 |
| - const dataView = convertToWav(soundFile.buffer); |
223 |
| - p5.prototype.writeFile([dataView], fileName, 'wav'); |
224 |
| -}; |
| 211 | +export default SoundRecorder; |
0 commit comments