@@ -469,158 +469,43 @@ export interface MediaElementLike extends MediaElementTapProvider {
469469 [ k : string ] : any ;
470470}
471471
472- const PLAYER_REF_ = Symbol ( '[[playerRef]]' ) ;
472+ const usedMediaElements : WeakSet < MediaElementLike > = new WeakSet ( ) ;
473473
474- export abstract class MediaElementAudioSourceBase {
475- protected [ context_ ] : BaseAudioContext ;
476- protected [ PLAYER_REF_ ] : MediaElementLike ;
477- protected _internalGain : any = null ;
478- protected _activeSource : any = null ;
479- protected _decoded : any = null ;
480- protected _decodingFor : string | null = null ;
481- protected _wasMuted = false ;
482- protected _originalVolume = 1 ;
483- protected _onPlayBound : ( ) => void ;
484- protected _onPauseBound : ( ) => void ;
485- protected _onEndedBound : ( ) => void ;
486- protected _tapNode : any = null ;
487- protected _usingTap = false ;
488-
489- constructor ( context : BaseAudioContext , mediaElement : MediaElementLike ) {
490- if ( ! ( context instanceof BaseAudioContext ) ) throw new TypeError ( 'MediaElementAudioSourceNode: invalid context' ) ;
491- if ( ! mediaElement || ( typeof mediaElement . play !== 'function' && typeof mediaElement . src !== 'string' ) ) {
492- throw new TypeError ( 'MediaElementAudioSourceNode: requires a media element with `src` and `play()`' ) ;
493- }
494- this [ context_ ] = context ;
495- this [ PLAYER_REF_ ] = mediaElement ;
496- this . _internalGain = this . _createGainNode ( ) ;
497-
498- if ( typeof mediaElement . attachAudioContextTap === 'function' ) {
499- try {
500- const tapNative = mediaElement . attachAudioContextTap ( ( this . context as any ) . native ) ;
501- if ( tapNative ) {
502- this . _tapNode = this . _wrapTapNative ( tapNative ) ;
503- if ( this . _tapNode ) {
504- this . _tapNode . connect ( this . _internalGain ) ;
505- this . _usingTap = true ;
506- }
507- }
508- } catch ( e ) { }
509- }
510-
511- this . _onPlayBound = ( ) => this . _handlePlay ( ) ;
512- this . _onPauseBound = ( ) => this . _handlePause ( ) ;
513- this . _onEndedBound = ( ) => this . _handleEnded ( ) ;
514- mediaElement . addEventListener ?.( 'play' , this . _onPlayBound ) ;
515- mediaElement . addEventListener ?.( 'pause' , this . _onPauseBound ) ;
516- mediaElement . addEventListener ?.( 'ended' , this . _onEndedBound ) ;
517- }
518- protected abstract _wrapTapNative ( tapNative : any ) : { connect ( t : any ) : void ; disconnect ( t ?: any ) : void ; stop ?( ) : void } | null ;
474+ export function resolveNativePlayer ( mediaElement : MediaElementLike ) : any {
475+ const m = mediaElement as any ;
476+ return m ?. helper ?. player ?? m ?. player ?? m ?. _player ?? null ;
477+ }
519478
520- get mediaElement ( ) : MediaElementLike {
521- return this [ PLAYER_REF_ ] ;
479+ export function assertMediaElementUsable ( context : BaseAudioContext , mediaElement : MediaElementLike ) : void {
480+ if ( ! ( context instanceof BaseAudioContext ) ) {
481+ throw new TypeError ( 'MediaElementAudioSourceNode: invalid context' ) ;
522482 }
523-
524- get context ( ) : BaseAudioContext {
525- return this [ context_ ] ;
483+ if ( ! mediaElement || ( typeof mediaElement . play !== 'function' && typeof mediaElement . src !== 'string' ) ) {
484+ throw new TypeError ( 'MediaElementAudioSourceNode: requires a media element with `src` and `play()`' ) ;
526485 }
527-
528- get _outputNode ( ) {
529- return this . _internalGain ;
486+ if ( usedMediaElements . has ( mediaElement ) ) {
487+ if ( typeof DOMException !== 'undefined' ) {
488+ throw new DOMException ( 'The media element has already been connected to an AudioContext' , 'InvalidStateError' ) ;
489+ }
490+ throw new Error ( 'The media element has already been connected to an AudioContext' ) ;
530491 }
492+ }
531493
532- connect ( target : any , output ?: number , input ?: number ) {
533- return this . _internalGain . connect ( target , output , input ) ;
534- }
535- disconnect ( target ?: any , output ?: number , input ?: number ) {
536- return this . _internalGain . disconnect ( target , output , input ) ;
537- }
494+ export function markMediaElementUsed ( mediaElement : MediaElementLike ) : void {
495+ try {
496+ usedMediaElements . add ( mediaElement ) ;
497+ } catch ( e ) { }
498+ }
538499
539- disposeMediaElementSource ( ) {
540- const el = this [ PLAYER_REF_ ] ;
541- try {
542- el . removeEventListener ?.( 'play' , this . _onPlayBound ) ;
543- el . removeEventListener ?.( 'pause' , this . _onPauseBound ) ;
544- el . removeEventListener ?.( 'ended' , this . _onEndedBound ) ;
545- } catch ( e ) { }
546- if ( this . _usingTap ) {
547- try {
548- el . detachAudioContextTap ?.( ) ;
549- } catch ( e ) { }
550- try {
551- this . _tapNode ?. disconnect ?.( ) ;
552- } catch ( e ) { }
553- this . _tapNode = null ;
554- this . _usingTap = false ;
555- }
556- this . _stopActive ( ) ;
557- this . _restoreVolume ( ) ;
558- }
559-
560- protected abstract _createGainNode ( ) : any ;
561- protected abstract _createBufferSource ( buffer : any ) : any ;
562- protected abstract _decodeFromUrl ( src : string ) : Promise < any > ;
563-
564- protected async _handlePlay ( ) {
565- if ( this . _usingTap ) return ;
566- const el = this [ PLAYER_REF_ ] ;
567- const src = el . src ?? '' ;
568- if ( ! src ) return ;
569- try {
570- if ( this . _decoded == null || this . _decodingFor !== src ) {
571- this . _decodingFor = src ;
572- this . _decoded = await this . _decodeFromUrl ( src ) ;
573- }
574- this . _muteOriginal ( ) ;
575- this . _stopActive ( ) ;
576- const source = this . _createBufferSource ( this . _decoded ) ;
577- source . loop = ! ! el . loop ;
578- source . connect ( this . _internalGain ) ;
579- source . start ( ) ;
580- this . _activeSource = source ;
581- } catch ( e ) { }
582- }
583-
584- protected _handlePause ( ) {
585- if ( this . _usingTap ) return ;
586- this . _stopActive ( ) ;
587- this . _restoreVolume ( ) ;
588- }
589-
590- protected _handleEnded ( ) {
591- if ( this . _usingTap ) return ;
592- this . _stopActive ( ) ;
593- this . _restoreVolume ( ) ;
594- }
595-
596- protected _stopActive ( ) {
597- const s = this . _activeSource ;
598- this . _activeSource = null ;
599- if ( ! s ) return ;
600- try {
601- s . stop ?.( ) ;
602- s . disconnect ?.( ) ;
603- } catch ( e ) { }
604- }
605-
606- protected _muteOriginal ( ) {
607- if ( this . _wasMuted ) return ;
608- const el = this [ PLAYER_REF_ ] ;
609- if ( typeof el . volume === 'number' ) {
610- this . _originalVolume = el . volume ;
611- try {
612- el . volume = 0 ;
613- this . _wasMuted = true ;
614- } catch ( e ) { }
615- }
616- }
500+ export function unmarkMediaElementUsed ( mediaElement : MediaElementLike ) : void {
501+ try {
502+ usedMediaElements . delete ( mediaElement ) ;
503+ } catch ( e ) { }
504+ }
617505
618- protected _restoreVolume ( ) {
619- if ( ! this . _wasMuted ) return ;
620- const el = this [ PLAYER_REF_ ] ;
621- try {
622- el . volume = this . _originalVolume ;
623- } catch ( e ) { }
624- this . _wasMuted = false ;
506+ export function throwInvalidMediaElement ( ) : never {
507+ if ( typeof DOMException !== 'undefined' ) {
508+ throw new DOMException ( 'Failed to attach a native source for the media element' , 'InvalidStateError' ) ;
625509 }
510+ throw new Error ( 'Failed to attach a native source for the media element' ) ;
626511}
0 commit comments