import { HttpClient } from '@angular/common/http' ; import { Injectable } from '@angular/core' ; import { Observable , Observer , of } from 'rxjs' ; @ Injectable ( ) export class AudioService { private _audioBuffer : AudioBuffer ; private _audioBufferSource : AudioBufferSourceNode ; private _audioContext : AudioContext ; private _gainNode : GainNode ; private _analyserNode : AnalyserNode ; constructor ( private _http ) { } getAudioFileMetadata ( mediaElement ) { return Observable . create ( ( observer ) => { this . _http . get ( mediaElement . src , { responseType : 'blob' } ) . subscribe ( response => { let description ; const extension = mediaElement . src . substring ( mediaElement . src . lastIndexOf ( '.' ) + 1 ) . toLowerCase ( ) ; const filename = mediaElement . src . indexOf ( 'base64' ) !== - 1 ? '' : mediaElement . src . substring ( mediaElement . src . lastIndexOf ( '/' ) + 1 ) ; switch ( extension ) { case 'mp3' : description = 'MPEG audio layer 3 file' ; break ; case 'wma' : description = 'Windows media audio file' ; break ; case 'wav' : description = 'WAVE audio file' ; break ; case 'ogg' : description = 'Ogg Vorbis file' ; break ; case 'aac' : description = 'Advanced audio coding file' ; break ; case 'midi' : description = 'Musical instrument digital interface file' ; break ; default : description = 'Audio file' ; break ; } observer . next ( { filename , extension , description , size : response . size } ) ; } ) ; } ) ; } getWaveformFromUrl ( url ) { if ( ! window . AudioContext ) { return of < Float32Array [ ] > ( [ new Float32Array ( 0 ) ] ) ; } this . _audioContext = new AudioContext ( ) ; this . createVolumeNode ( ) ; this . createAnalyserNode ( ) ; return Observable . create ( ( observer ) => { this . _http . get ( url , { responseType : 'arraybuffer' } ) . subscribe ( response => { this . getAudioBuffer ( response ) . subscribe ( audioBuffer => { this . createBufferSource ( audioBuffer ) ; let dataPoints = [ ] ; const channels = this . _audioBuffer . numberOfChannels ; for ( let channelIdx = 0 ; channelIdx < channels ; channelIdx ++ ) { dataPoints [ channelIdx ] = this . _audioBuffer . getChannelData ( channelIdx ) ; } observer . next ( dataPoints ) ; observer . complete ( ) ; dataPoints = null ; } , ( error ) => observer . error ( error ) ) ; } , ( error ) => observer . error ( error ) ) ; } ) ; } getWaveformPoints ( channels = [ ] , skip = 1000 ) { const waveform = [ ] ; const duration = channels . length > 0 ? channels [ 0 ] . length : 0 ; for ( let idx = 0 ; idx < duration ; idx += skip ) { const points = channels . map ( channel => channel [ idx ] ) ; waveform . push ( { min : points . reduce ( ( previous , current ) => current < previous ? current : previous ) , max : points . reduce ( ( previous , current ) => current > previous ? current : previous ) } ) ; } return waveform ; } private getAudioBuffer ( arrayBuffer ) { return Observable . create ( ( observer ) => { this . getOfflineAudioContext ( ) . decodeAudioData ( arrayBuffer , ( audioBuffer ) => { observer . next ( audioBuffer ) ; observer . complete ( ) ; } , ( error ) => observer . error ( error ) ) ; } ) ; } private getOfflineAudioContext ( ) { return new OfflineAudioContext ( 1 , 2 , this . _audioContext . sampleRate || 44100 ) ; } private createBufferSource ( audioBuffer ) { this . disconnectSource ( ) ; this . _audioBuffer = audioBuffer ; this . _audioBufferSource = this . _audioContext . createBufferSource ( ) ; this . _audioBufferSource . buffer = this . _audioBuffer ; this . _audioBufferSource . connect ( this . _analyserNode ) ; } private createVolumeNode ( ) { this . _gainNode = this . _audioContext . createGain ( ) ; this . _gainNode . connect ( this . _audioContext . destination ) ; } private createAnalyserNode ( ) { this . _analyserNode = this . _audioContext . createAnalyser ( ) ; this . _analyserNode . connect ( this . _gainNode ) ; } private disconnectSource ( ) { if ( this . _audioBufferSource ) { this . _audioBufferSource . disconnect ( ) ; } } } export interface WaveformPoint { min : number ; max : number ; } export interface AudioMetadata { filename : string ; extension : string ; description : string ; size : number ; }