Skip to content

Commit

Permalink
fix: [#2450] Sound looping regression (#2451)
Browse files Browse the repository at this point in the history
Closes #2450

## Changes:

- Avoids setting a `duration` on the underlying web audio instance, this causes odd behavior in looping and playing
  • Loading branch information
eonarheim authored Aug 5, 2022
1 parent f7ea021 commit 5c96867
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).

### Fixed

- Fixed issue where `ex.Sound.loop` was not working, and switching tab visibility would cause odd behavior with looping `ex.Sound`
- Fixed issue where screenshots from `ex.Engine.screenshot()` did not match the smoothing set on the engine.
- Fixed incorrect event type returned when `ex.Actor.on('postupdate', (event) => {...})`.
- Fixed issue where using numerous `ex.Text` instances would cause Excalibur to crash webgl by implementing a global font cache.
Expand Down
13 changes: 13 additions & 0 deletions sandbox/tests/sound/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@
<title>Sound</title>
</head>
<body>
<label for="loop">Loop</label>
<input id="loop" type="checkbox">
<label for="playback-rate">Set playback rate</label>
<input
id="playback-rate"
class="playback-rate-control"
type="range"
min="0.25"
max="3"
step="0.05"
value="1"
/>
<span id="playback-rate-value">1.0</span>
<script src="../../lib/excalibur.js"></script>
<script src="sound.js"></script>
</body>
Expand Down
Binary file added sandbox/tests/sound/loop.mp3
Binary file not shown.
14 changes: 12 additions & 2 deletions sandbox/tests/sound/sound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ var game = new ex.Engine({



var sound = new ex.Sound('./preview.ogg');
sound.playbackRate = 2.0;
var sound = new ex.Sound('./loop.mp3');
var loader = new ex.Loader([
sound
]);
Expand Down Expand Up @@ -73,6 +72,17 @@ pause.on('pointerdown', () => {
});
game.currentScene.add(pause);

var loopCheckbox = document.querySelector('#loop') as HTMLInputElement;
loopCheckbox.onchange = (e) => {
sound.loop = !!(e.target as any).value;
}

var playbackRate = document.querySelector('#playback-rate') as HTMLInputElement;
var playbackValue = document.querySelector('#playback-rate-value') as HTMLInputElement;
playbackRate.oninput = () => {
sound.playbackRate = +playbackRate.value;
playbackValue.textContent = playbackRate.value;
};

game.currentScene.onPostUpdate = () => {
currentTimeLabel.text = 'Current Time: ' + sound.getPlaybackPosition().toFixed(2);
Expand Down
7 changes: 6 additions & 1 deletion src/engine/Resources/Sound/WebAudioInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ export class WebAudioInstance implements Audio {
// Buffer nodes are single use
this._createNewBufferSource();
this._handleEnd();
this._instance.start(0, data.pausedAt * this._playbackRate, this.duration);
if (this.loop) {
// when looping don't set a duration
this._instance.start(0, data.pausedAt * this._playbackRate);
} else {
this._instance.start(0, data.pausedAt * this._playbackRate, this.duration);
}
data.startedAt = (this._audioContext.currentTime - data.pausedAt);
data.pausedAt = 0;
},
Expand Down
28 changes: 28 additions & 0 deletions src/spec/SoundSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,34 @@ describe('Sound resource', () => {
});
});

it('should not provide a duration if looping', async () => {
await sut.load();

const webaudio = new ex.WebAudioInstance(sut.data);
spyOn(webaudio as any, '_createNewBufferSource');
const instance = jasmine.createSpyObj('AudioBufferSourceNode', ['start']);
(webaudio as any)._instance = instance;
webaudio.loop = true;
webaudio.play();

expect((webaudio as any)._createNewBufferSource).toHaveBeenCalled();
expect(instance.start).toHaveBeenCalledWith(0, 0);
});

it('should provide a duration if not looping', async () => {
await sut.load();

const webaudio = new ex.WebAudioInstance(sut.data);
spyOn(webaudio as any, '_createNewBufferSource');
const instance = jasmine.createSpyObj('AudioBufferSourceNode', ['start']);
(webaudio as any)._instance = instance;
webaudio.loop = false;
webaudio.play();

expect((webaudio as any)._createNewBufferSource).toHaveBeenCalled();
expect(instance.start).toHaveBeenCalledWith(0, 0, sut.duration);
});

it('should set tracks volume value same as own', (done) => {
sut.load().then(() => {
sut.volume = 0.5;
Expand Down

0 comments on commit 5c96867

Please sign in to comment.