import { BANDS , MEDIA_STATUS } from "../constants" ; import { Band } from "../types" ; import Emitter from "../emitter" ; import StereoBalanceNode from "./StereoBalanceNode" ; import ElementSource from "./elementSource" ; interface StereoBalanceNodeType extends AudioNode { constructor ( context ) : StereoBalanceNodeType ; balance : { value : number ; } ; } export default class Media { _emitter : Emitter ; _context : AudioContext ; _balance : StereoBalanceNodeType ; _staticSource : GainNode ; _preamp : GainNode ; _analyser : AnalyserNode ; _gainNode : GainNode ; _source : ElementSource ; _bands : { [ band ] : BiquadFilterNode ; } ; constructor ( ) { this . _emitter = new Emitter ( ) ; this . _context = new ( window . AudioContext || window . webkitAudioContext ) ( ) ; if ( this . _context . state === "suspended" ) { const resume = async ( ) => { await this . _context . resume ( ) ; if ( this . _context . state === "running" ) { document . body . removeEventListener ( "touchend" , resume , false ) ; document . body . removeEventListener ( "click" , resume , false ) ; document . body . removeEventListener ( "keydown" , resume , false ) ; } } ; document . body . addEventListener ( "touchend" , resume , false ) ; document . body . addEventListener ( "click" , resume , false ) ; document . body . addEventListener ( "keydown" , resume , false ) ; } this . _staticSource = this . _context . createGain ( ) ; this . _balance = new StereoBalanceNode ( this . _context ) ; this . _preamp = this . _context . createGain ( ) ; this . _analyser = this . _context . createAnalyser ( ) ; this . _analyser . fftSize = 2048 ; this . _analyser . smoothingTimeConstant = 0.0 ; this . _gainNode = this . _context . createGain ( ) ; this . _source = new ElementSource ( this . _context , this . _staticSource ) ; this . _source . on ( "positionChange" , ( ) => { this . _emitter . trigger ( "timeupdate" ) ; } ) ; this . _source . on ( "ended" , ( ) => { this . _emitter . trigger ( "ended" ) ; } ) ; this . _source . on ( "statusChange" , ( ) => { switch ( this . _source . getStatus ( ) ) { case MEDIA_STATUS . PLAYING : this . _emitter . trigger ( "playing" ) ; break ; } this . _emitter . trigger ( "timeupdate" ) ; } ) ; this . _source . on ( "loaded" , ( ) => { this . _emitter . trigger ( "fileLoaded" ) ; } ) ; this . _staticSource . connect ( this . _preamp ) ; let output = this . _preamp ; this . _bands = { } ; BANDS . forEach ( ( band , i ) => { const filter = this . _context . createBiquadFilter ( ) ; this . _bands [ band ] = filter ; if ( i === 0 ) { filter . type = "lowshelf" ; } else if ( i === BANDS . length - 1 ) { filter . type = "highshelf" ; } else { filter . type = "peaking" ; } filter . frequency . value = band ; filter . gain . value = 0 ; output . connect ( filter ) ; output = filter ; } ) ; output . connect ( this . _balance ) ; this . _balance . connect ( this . _gainNode ) ; this . _balance . connect ( this . _analyser ) ; this . _gainNode . connect ( this . _context . destination ) ; } getAnalyser ( ) { return this . _analyser ; } duration ( ) { return this . _source . getDuration ( ) ; } timeElapsed ( ) { return this . _source . getTimeElapsed ( ) ; } timeRemaining ( ) { return this . duration ( ) - this . timeElapsed ( ) ; } percentComplete ( ) { return ( this . timeElapsed ( ) / this . duration ( ) ) * 100 ; } async play ( ) { await this . _source . play ( ) ; } pause ( ) { this . _source . pause ( ) ; } stop ( ) { this . _source . stop ( ) ; } seekToPercentComplete ( percent ) { const seekTime = this . duration ( ) * ( percent / 100 ) ; this . seekToTime ( seekTime ) ; } setVolume ( volume ) { this . _gainNode . gain . value = volume / 100 ; } setPreamp ( value ) { const db = ( value / 100 ) * 24 - 12 ; this . _preamp . gain . value = Math . pow ( 10 , db / 20 ) ; } setBalance ( balance ) { this . _balance . balance . value = balance / 100 ; } setEqBand ( band , value ) { const db = ( value / 100 ) * 24 - 12 ; this . _bands [ band ] . gain . value = db ; } disableEq ( ) { this . _staticSource . disconnect ( ) ; this . _staticSource . connect ( this . _balance ) ; } enableEq ( ) { this . _staticSource . disconnect ( ) ; this . _staticSource . connect ( this . _preamp ) ; } on ( event , callback ) { this . _emitter . on ( event , callback ) ; } seekToTime ( time ) { this . _source . seekToTime ( time ) ; } async loadFromUrl ( url , autoPlay ) { this . _emitter . trigger ( "waiting" ) ; await this . _source . loadUrl ( url ) ; this . _emitter . trigger ( "stopWaiting" ) ; if ( autoPlay ) { this . play ( ) ; } } dispose ( ) { this . _source . dispose ( ) ; this . _emitter . dispose ( ) ; } }