Skip to content

Commit 7980c23

Browse files
committed
Make submitNewSamples async and use promises
1 parent 5e4a2b7 commit 7980c23

File tree

3 files changed

+87
-77
lines changed

3 files changed

+87
-77
lines changed

src/containers/sound-editor.jsx

Lines changed: 71 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -132,45 +132,34 @@ class SoundEditor extends React.Component {
132132
});
133133
}
134134
submitNewSamples (samples, sampleRate, skipUndo) {
135-
// Encode the new sound into a wav so that it can be stored
136-
let wavBuffer = null;
137-
try {
138-
wavBuffer = WavEncoder.encode.sync({
139-
sampleRate: sampleRate,
140-
channelData: [samples]
141-
});
142-
143-
if (wavBuffer.byteLength > SOUND_BYTE_LIMIT) {
144-
// Cancel the sound update by setting to null
145-
wavBuffer = null;
146-
log.error(`Refusing to encode sound larger than ${SOUND_BYTE_LIMIT} bytes`);
147-
}
148-
} catch (e) {
149-
// This error state is mostly for the mock sounds used during testing.
150-
// Any incorrect sound buffer trying to get interpretd as a Wav file
151-
// should yield this error.
152-
// This can also happen if the sound is too be allocated in memory.
153-
log.error(`Encountered error while trying to encode sound update: ${e}`);
154-
}
155-
156-
// Do not submit sound if it could not be encoded (i.e. if too large)
157-
if (wavBuffer) {
158-
if (!skipUndo) {
159-
this.redoStack = [];
160-
if (this.undoStack.length >= UNDO_STACK_SIZE) {
161-
this.undoStack.shift(); // Drop the first element off the array
135+
return WavEncoder.encode({
136+
sampleRate: sampleRate,
137+
channelData: [samples]
138+
})
139+
.then(wavBuffer => {
140+
if (wavBuffer.byteLength > SOUND_BYTE_LIMIT) {
141+
log.error(`Refusing to encode sound larger than ${SOUND_BYTE_LIMIT} bytes`);
142+
return Promise.reject();
162143
}
163-
this.undoStack.push(this.getUndoItem());
164-
}
165-
this.resetState(samples, sampleRate);
166-
this.props.vm.updateSoundBuffer(
167-
this.props.soundIndex,
168-
this.audioBufferPlayer.buffer,
169-
new Uint8Array(wavBuffer));
170-
171-
return true; // Update succeeded
172-
}
173-
return false; // Update failed
144+
if (!skipUndo) {
145+
this.redoStack = [];
146+
if (this.undoStack.length >= UNDO_STACK_SIZE) {
147+
this.undoStack.shift(); // Drop the first element off the array
148+
}
149+
this.undoStack.push(this.getUndoItem());
150+
}
151+
this.resetState(samples, sampleRate);
152+
this.props.vm.updateSoundBuffer(
153+
this.props.soundIndex,
154+
this.audioBufferPlayer.buffer,
155+
new Uint8Array(wavBuffer));
156+
return true; // Edit was successful
157+
})
158+
.catch(e => {
159+
// Encoding failed, or the sound was too large to save so edit is rejected
160+
log.error(`Encountered error while trying to encode sound update: ${e}`);
161+
return false; // Edit was not applied
162+
});
174163
}
175164
handlePlay () {
176165
this.audioBufferPlayer.stop();
@@ -209,11 +198,13 @@ class SoundEditor extends React.Component {
209198
newSamples.set(firstPart, 0);
210199
newSamples.set(secondPart, firstPart.length);
211200
}
212-
this.submitNewSamples(newSamples, sampleRate);
213-
this.setState({
214-
trimStart: null,
215-
trimEnd: null
201+
this.submitNewSamples(newSamples, sampleRate).then(() => {
202+
this.setState({
203+
trimStart: null,
204+
trimEnd: null
205+
});
216206
});
207+
217208
}
218209
handleDeleteInverse () {
219210
const {samples, sampleRate} = this.copyCurrentBuffer();
@@ -224,10 +215,13 @@ class SoundEditor extends React.Component {
224215
if (clippedSamples.length === 0) {
225216
clippedSamples = new Float32Array(1);
226217
}
227-
this.submitNewSamples(clippedSamples, sampleRate);
228-
this.setState({
229-
trimStart: null,
230-
trimEnd: null
218+
this.submitNewSamples(clippedSamples, sampleRate).then(success => {
219+
if (success) {
220+
this.setState({
221+
trimStart: null,
222+
trimEnd: null
223+
});
224+
}
231225
});
232226
}
233227
handleUpdateTrim (trimStart, trimEnd) {
@@ -257,14 +251,15 @@ class SoundEditor extends React.Component {
257251
effects.process((renderedBuffer, adjustedTrimStart, adjustedTrimEnd) => {
258252
const samples = renderedBuffer.getChannelData(0);
259253
const sampleRate = renderedBuffer.sampleRate;
260-
const success = this.submitNewSamples(samples, sampleRate);
261-
if (success) {
262-
if (this.state.trimStart === null) {
263-
this.handlePlay();
264-
} else {
265-
this.setState({trimStart: adjustedTrimStart, trimEnd: adjustedTrimEnd}, this.handlePlay);
254+
this.submitNewSamples(samples, sampleRate).then(success => {
255+
if (success) {
256+
if (this.state.trimStart === null) {
257+
this.handlePlay();
258+
} else {
259+
this.setState({trimStart: adjustedTrimStart, trimEnd: adjustedTrimEnd}, this.handlePlay);
260+
}
266261
}
267-
}
262+
});
268263
});
269264
}
270265
tooLoud () {
@@ -287,16 +282,22 @@ class SoundEditor extends React.Component {
287282
this.redoStack.push(this.getUndoItem());
288283
const {samples, sampleRate, trimStart, trimEnd} = this.undoStack.pop();
289284
if (samples) {
290-
this.submitNewSamples(samples, sampleRate, true);
291-
this.setState({trimStart: trimStart, trimEnd: trimEnd}, this.handlePlay);
285+
return this.submitNewSamples(samples, sampleRate, true).then(success => {
286+
if (success) {
287+
this.setState({trimStart: trimStart, trimEnd: trimEnd}, this.handlePlay);
288+
}
289+
});
292290
}
293291
}
294292
handleRedo () {
295293
const {samples, sampleRate, trimStart, trimEnd} = this.redoStack.pop();
296294
if (samples) {
297295
this.undoStack.push(this.getUndoItem());
298-
this.submitNewSamples(samples, sampleRate, true);
299-
this.setState({trimStart: trimStart, trimEnd: trimEnd}, this.handlePlay);
296+
return this.submitNewSamples(samples, sampleRate, true).then(success => {
297+
if (success) {
298+
this.setState({trimStart: trimStart, trimEnd: trimEnd}, this.handlePlay);
299+
}
300+
});
300301
}
301302
}
302303
handleCopy () {
@@ -351,8 +352,11 @@ class SoundEditor extends React.Component {
351352
const newSamples = new Float32Array(newLength);
352353
newSamples.set(samples, 0);
353354
newSamples.set(this.state.copyBuffer.samples, samples.length);
354-
this.submitNewSamples(newSamples, this.props.sampleRate, false);
355-
this.handlePlay();
355+
this.submitNewSamples(newSamples, this.props.sampleRate, false).then(success => {
356+
if (success) {
357+
this.handlePlay();
358+
}
359+
});
356360
} else {
357361
// else replace the selection with the pasted sound
358362
const trimStartSamples = this.state.trimStart * samples.length;
@@ -371,11 +375,14 @@ class SoundEditor extends React.Component {
371375
const newDurationSeconds = newSamples.length / this.state.copyBuffer.sampleRate;
372376
const adjustedTrimStart = trimStartSeconds / newDurationSeconds;
373377
const adjustedTrimEnd = trimEndSeconds / newDurationSeconds;
374-
this.submitNewSamples(newSamples, this.props.sampleRate, false);
375-
this.setState({
376-
trimStart: adjustedTrimStart,
377-
trimEnd: adjustedTrimEnd
378-
}, this.handlePlay);
378+
this.submitNewSamples(newSamples, this.props.sampleRate, false).then(success => {
379+
if (success) {
380+
this.setState({
381+
trimStart: adjustedTrimStart,
382+
trimEnd: adjustedTrimEnd
383+
}, this.handlePlay);
384+
}
385+
});
379386
}
380387
}
381388
handlePaste () {

test/__mocks__/audio-effects.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ export default class MockAudioEffects {
1414
this.buffer = buffer;
1515
this.name = name;
1616
this.process = jest.fn(done => {
17-
this._finishProcessing = renderedBuffer => done(renderedBuffer, 0, 1);
17+
this._finishProcessing = renderedBuffer => {
18+
done(renderedBuffer, 0, 1);
19+
return new Promise(resolve => setTimeout(resolve));
20+
};
1821
});
1922
MockAudioEffects.instance = this;
2023
}

test/unit/containers/sound-editor.test.jsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ describe('Sound Editor Container', () => {
9797
expect(vm.renameSound).toHaveBeenCalledWith(soundIndex, 'hello');
9898
});
9999

100-
test('it handles an effect by submitting the result and playing', () => {
100+
test('it handles an effect by submitting the result and playing', async () => {
101101
const wrapper = mountWithIntl(
102102
<SoundEditor
103103
soundIndex={soundIndex}
@@ -106,7 +106,7 @@ describe('Sound Editor Container', () => {
106106
);
107107
const component = wrapper.find(SoundEditorComponent);
108108
component.props().onReverse(); // Could be any of the effects, just testing the end result
109-
mockAudioEffects.instance._finishProcessing(soundBuffer);
109+
await mockAudioEffects.instance._finishProcessing(soundBuffer);
110110
expect(mockAudioBufferPlayer.instance.play).toHaveBeenCalled();
111111
expect(vm.updateSoundBuffer).toHaveBeenCalled();
112112
});
@@ -202,7 +202,7 @@ describe('Sound Editor Container', () => {
202202
expect(mockAudioEffects.instance.process).toHaveBeenCalled();
203203
});
204204

205-
test('undo/redo stack state', () => {
205+
test('undo/redo stack state', async () => {
206206
const wrapper = mountWithIntl(
207207
<SoundEditor
208208
soundIndex={soundIndex}
@@ -216,40 +216,40 @@ describe('Sound Editor Container', () => {
216216

217217
// Submitting new samples should make it possible to undo
218218
component.props().onFaster();
219-
mockAudioEffects.instance._finishProcessing(soundBuffer);
219+
await mockAudioEffects.instance._finishProcessing(soundBuffer);
220220
wrapper.update();
221221
component = wrapper.find(SoundEditorComponent);
222222
expect(component.prop('canUndo')).toEqual(true);
223223
expect(component.prop('canRedo')).toEqual(false);
224224

225225
// Undoing should make it possible to redo and not possible to undo again
226-
component.props().onUndo();
226+
await component.props().onUndo();
227227
wrapper.update();
228228
component = wrapper.find(SoundEditorComponent);
229229
expect(component.prop('canUndo')).toEqual(false);
230230
expect(component.prop('canRedo')).toEqual(true);
231231

232232
// Redoing should make it possible to undo and not possible to redo again
233-
component.props().onRedo();
233+
await component.props().onRedo();
234234
wrapper.update();
235235
component = wrapper.find(SoundEditorComponent);
236236
expect(component.prop('canUndo')).toEqual(true);
237237
expect(component.prop('canRedo')).toEqual(false);
238238

239239
// New submission should clear the redo stack
240-
component.props().onUndo(); // Undo to go back to a state where redo is enabled
240+
await component.props().onUndo(); // Undo to go back to a state where redo is enabled
241241
wrapper.update();
242242
component = wrapper.find(SoundEditorComponent);
243243
expect(component.prop('canRedo')).toEqual(true);
244244
component.props().onFaster();
245-
mockAudioEffects.instance._finishProcessing(soundBuffer);
245+
await mockAudioEffects.instance._finishProcessing(soundBuffer);
246246

247247
wrapper.update();
248248
component = wrapper.find(SoundEditorComponent);
249249
expect(component.prop('canRedo')).toEqual(false);
250250
});
251251

252-
test('undo and redo submit new samples and play the sound', () => {
252+
test('undo and redo submit new samples and play the sound', async () => {
253253
const wrapper = mountWithIntl(
254254
<SoundEditor
255255
soundIndex={soundIndex}
@@ -260,12 +260,12 @@ describe('Sound Editor Container', () => {
260260

261261
// Set up an undoable state
262262
component.props().onFaster();
263-
mockAudioEffects.instance._finishProcessing(soundBuffer);
263+
await mockAudioEffects.instance._finishProcessing(soundBuffer);
264264
wrapper.update();
265265
component = wrapper.find(SoundEditorComponent);
266266

267267
// Undo should update the sound buffer and play the new samples
268-
component.props().onUndo();
268+
await component.props().onUndo();
269269
expect(mockAudioBufferPlayer.instance.play).toHaveBeenCalled();
270270
expect(vm.updateSoundBuffer).toHaveBeenCalled();
271271

@@ -274,7 +274,7 @@ describe('Sound Editor Container', () => {
274274
mockAudioBufferPlayer.instance.play.mockClear();
275275

276276
// Undo should update the sound buffer and play the new samples
277-
component.props().onRedo();
277+
await component.props().onRedo();
278278
expect(mockAudioBufferPlayer.instance.play).toHaveBeenCalled();
279279
expect(vm.updateSoundBuffer).toHaveBeenCalled();
280280
});

0 commit comments

Comments
 (0)