parent
179c7a8158
commit
27ef3cc1c9
1 changed files with 82 additions and 2 deletions
|
@ -33,9 +33,89 @@ export default class AudioPlaybackController implements IPlaybackController {
|
||||||
this._mainGain.connect(this._nodeDestination)
|
this._mainGain.connect(this._nodeDestination)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement this method in the future!
|
|
||||||
playDynamicRange(rangeD: IDerivation<IPlaybackRange>): Promise<unknown> {
|
playDynamicRange(rangeD: IDerivation<IPlaybackRange>): Promise<unknown> {
|
||||||
throw new Error('Method not implemented.')
|
const deferred = defer<boolean>()
|
||||||
|
if (this._playing) this.pause()
|
||||||
|
|
||||||
|
this._playing = true
|
||||||
|
|
||||||
|
let stop: undefined | (() => void) = undefined
|
||||||
|
|
||||||
|
const play = () => {
|
||||||
|
stop?.()
|
||||||
|
stop = this._loopInRange(rangeD.getValue()).stop
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're keeping the rangeD hot, so we can read from it on every tick without
|
||||||
|
// causing unnecessary recalculations
|
||||||
|
const untapFromRangeD = rangeD.changesWithoutValues().tap(play)
|
||||||
|
play()
|
||||||
|
|
||||||
|
this._stopPlayCallback = () => {
|
||||||
|
stop?.()
|
||||||
|
untapFromRangeD()
|
||||||
|
deferred.resolve(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return deferred.promise
|
||||||
|
}
|
||||||
|
|
||||||
|
private _loopInRange(range: IPlaybackRange): {stop: () => void} {
|
||||||
|
const rate = 1
|
||||||
|
const ticker = this._ticker
|
||||||
|
let startPos = this.getCurrentPosition()
|
||||||
|
const iterationLength = range[1] - range[0]
|
||||||
|
|
||||||
|
if (startPos < range[0] || startPos > range[1]) {
|
||||||
|
// if we're currently out of the range
|
||||||
|
this._updatePositionInState(range[0])
|
||||||
|
} else if (startPos === range[1]) {
|
||||||
|
// if we're currently at the very end of the range
|
||||||
|
this._updatePositionInState(range[0])
|
||||||
|
}
|
||||||
|
startPos = this.getCurrentPosition()
|
||||||
|
|
||||||
|
const currentSource = this._audioContext.createBufferSource()
|
||||||
|
currentSource.buffer = this._decodedBuffer
|
||||||
|
currentSource.connect(this._mainGain)
|
||||||
|
currentSource.playbackRate.value = rate
|
||||||
|
|
||||||
|
currentSource.loop = true
|
||||||
|
currentSource.loopStart = range[0]
|
||||||
|
currentSource.loopEnd = range[1]
|
||||||
|
|
||||||
|
const initialTickerTime = ticker.time
|
||||||
|
let initialElapsedPos = startPos - range[0]
|
||||||
|
|
||||||
|
currentSource.start(0, startPos)
|
||||||
|
|
||||||
|
const tick = (currentTickerTime: number) => {
|
||||||
|
const elapsedTickerTime = Math.max(
|
||||||
|
currentTickerTime - initialTickerTime,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
const elapsedTickerTimeInSeconds = elapsedTickerTime / 1000
|
||||||
|
|
||||||
|
const elapsedPos = elapsedTickerTimeInSeconds * rate + initialElapsedPos
|
||||||
|
|
||||||
|
let currentIterationPos =
|
||||||
|
((elapsedPos / iterationLength) % 1) * iterationLength
|
||||||
|
|
||||||
|
this._updatePositionInState(currentIterationPos + range[0])
|
||||||
|
requestNextTick()
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestNextTick = () => ticker.onNextTick(tick)
|
||||||
|
ticker.onThisOrNextTick(tick)
|
||||||
|
|
||||||
|
const stop = () => {
|
||||||
|
currentSource.stop()
|
||||||
|
currentSource.disconnect()
|
||||||
|
ticker.offThisOrNextTick(tick)
|
||||||
|
ticker.offNextTick(tick)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {stop}
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _playing() {
|
private get _playing() {
|
||||||
|
|
Loading…
Reference in a new issue