import { LoopNode } from 'nf-grapher' ; import { TimeInstant } from '../time' ; import { SPNode , NodePlaybackDescription } from './SPNode' ; import { mixdown , copy } from '../AudioBufferUtils' ; import { SPNodeFactory } from './SPNodeFactory' ; import { XAudioBuffer } from '../XAudioBuffer' ; import { XAudioBufferFromInfo } from '../renderers/RendererInfo' ; export class SPLoopNode extends SPNode { getPlaybackDescription ( renderTime , descriptions ) { const node = LoopNode . from ( this . node ) ; const loopDuration = TimeInstant . fromNanos ( node . duration ) ; const loopWhen = TimeInstant . fromNanos ( node . when ) ; const loopsSinceStart = renderTime . sub ( loopWhen ) . div ( loopDuration ) ; let currentLoopStartTime ; if ( loopsSinceStart < 0 ) { currentLoopStartTime = loopWhen ; } else { currentLoopStartTime = loopWhen . add ( loopDuration . scale ( Math . floor ( loopsSinceStart ) ) ) ; } const currentLoopEndTime = currentLoopStartTime . add ( loopDuration ) ; const loopElapsedTime = renderTime . sub ( currentLoopStartTime ) ; const infinite = node . loopCount === - 1 ; let ancestorRenderTime = renderTime ; if ( renderTime . gte ( loopWhen ) && node . loopCount === - 1 ) { ancestorRenderTime = loopWhen . add ( loopElapsedTime ) ; } else if ( renderTime . gte ( loopWhen ) && node . loopCount !== - 1 ) { const unrolledDuration = loopDuration . scale ( node . loopCount ) ; const loopEndTime = loopWhen . add ( unrolledDuration ) ; ancestorRenderTime = renderTime . gte ( loopEndTime ) ? renderTime : loopWhen . add ( loopElapsedTime ) ; } const desc = { id : this . node . id , kind : this . node . kind , time : ancestorRenderTime , loop : { loopElapsedTime , loopsSinceStart , currentLoopStartTime , currentLoopEndTime , infinite } } ; descriptions . push ( desc ) ; SPNodeFactory . getPlaybackDescription ( this . ancestors , ancestorRenderTime , descriptions ) ; } dilateSampleIndex ( sampleIndex ) { const hz = this . info . sampleRate ; const node = LoopNode . from ( this . node ) ; const loopDuration = TimeInstant . fromNanos ( node . duration ) ; const loopWhen = TimeInstant . fromNanos ( node . when ) ; const startSampleIndex = loopWhen . asSamples ( hz ) ; const durationSamples = loopDuration . asSamples ( hz ) ; if ( sampleIndex < startSampleIndex ) { return sampleIndex ; } if ( node . loopCount > 0 ) { const loopedSamples = durationSamples * node . loopCount ; const endSampleIndex = startSampleIndex + loopedSamples ; if ( sampleIndex >= endSampleIndex ) { const extraSamples = loopedSamples - durationSamples ; return sampleIndex - extraSamples ; } } return ( ( ( sampleIndex - startSampleIndex ) % durationSamples ) + startSampleIndex ) ; } nextSampleCount ( sampleIndex , sampleCount ) { const hz = this . info . sampleRate ; const node = LoopNode . from ( this . node ) ; const loopDuration = TimeInstant . fromNanos ( node . duration ) ; const loopWhen = TimeInstant . fromNanos ( node . when ) ; const startSampleIndex = loopWhen . asSamples ( hz ) ; const durationSamples = loopDuration . asSamples ( hz ) ; const dilatedSampleIndex = this . dilateSampleIndex ( sampleIndex ) ; const dilatedEndSampleIndex = dilatedSampleIndex + sampleCount ; const dilatedEndLoopIndex = startSampleIndex + durationSamples ; const undilatedEndLoopIndex = startSampleIndex + durationSamples * node . loopCount ; const dilatedStartIndex = this . dilateSampleIndex ( startSampleIndex ) ; const inLoop = sampleIndex >= startSampleIndex && ( node . loopCount > 0 ? sampleIndex < undilatedEndLoopIndex : true ) ; if ( inLoop && dilatedEndSampleIndex > dilatedEndLoopIndex ) { const extraSamples = dilatedEndSampleIndex - dilatedEndLoopIndex ; const newSampleCount = sampleCount - extraSamples ; return newSampleCount ; } else if ( ! inLoop && dilatedStartIndex > sampleIndex && dilatedStartIndex < sampleIndex + sampleCount ) { const newSampleCount = dilatedStartIndex - sampleIndex ; return newSampleCount ; } else if ( inLoop && dilatedEndLoopIndex < dilatedEndSampleIndex ) { const newSampleCount = dilatedEndLoopIndex - dilatedSampleIndex ; return newSampleCount ; } return sampleCount ; } feed ( renderTime , buffers , sampleCount ) { const hz = this . info . sampleRate ; const renderTimeSampleIndex = renderTime . asSamples ( hz ) ; let sampleIndex = renderTimeSampleIndex ; let receivedSampleCount = 0 ; const outputs = [ ] ; while ( receivedSampleCount < sampleCount ) { let startIndex = sampleIndex ; let count = this . nextSampleCount ( sampleIndex , sampleCount ) ; if ( receivedSampleCount + count > sampleCount ) { count = sampleCount - receivedSampleCount ; } const ancestorRenderTimeSamples = this . dilateSampleIndex ( startIndex ) ; const ancestorRenderTime = TimeInstant . fromSamples ( ancestorRenderTimeSamples , hz ) ; const ancestorBuffers = [ ] ; const ancestorFrameSize = count ; SPNodeFactory . feed ( this . ancestors , ancestorRenderTime , ancestorBuffers , ancestorFrameSize ) ; sampleIndex += count ; receivedSampleCount += count ; const silence = XAudioBufferFromInfo ( this . info , ancestorFrameSize ) ; const mixed = mixdown ( silence , ancestorBuffers ) ; outputs . push ( mixed ) ; } const output = XAudioBufferFromInfo ( this . info , sampleCount ) ; let startIndex = 0 ; for ( let i = 0 ; i < outputs . length ; i ++ ) { const source = outputs [ i ] ; copy ( output , source , startIndex ) ; startIndex = source . length ; } buffers . push ( output ) ; } }