const EventEmitter = require('events').EventEmitter;
const Prism = require('prism-media');
const StreamDispatcher = require('../dispatcher/StreamDispatcher');
const Collection = require('../../../util/Collection');
const OpusEncoders = require('../opus/OpusEngineList');

const ffmpegArguments = [
  '-analyzeduration', '0',
  '-loglevel', '0',
  '-f', 's16le',
  '-ar', '48000',
  '-ac', '2',
];

/**
 * An Audio Player for a Voice Connection
 * @private
 * @extends {EventEmitter}
 */
class AudioPlayer extends EventEmitter {
  constructor(voiceConnection) {
    super();
    /**
     * The voice connection that the player serves
     * @type {VoiceConnection}
     */
    this.voiceConnection = voiceConnection;
    /**
     * The prism transcoder that the player uses
     * @type {Prism}
     */
    this.prism = new Prism();
    /**
     * The opus encoder that the player uses
     * @type {NodeOpusEngine|OpusScriptEngine}
     */
    this.opusEncoder = OpusEncoders.fetch();
    this.streams = new Collection();
    this.streamingData = {
      channels: 2,
      count: 0,
      sequence: 0,
      timestamp: 0,
      pausedTime: 0,
    };
    this.voiceConnection.once('closing', () => this.destroyAllStreams());
  }

  get currentTranscoder() {
    return this.streams.last().transcoder;
  }

  destroy() {
    if (this.opusEncoder) this.opusEncoder.destroy();
  }

  destroyStream(stream) {
    const data = this.streams.get(stream);
    if (!data) return;
    const transcoder = data.transcoder;
    const dispatcher = data.dispatcher;
    if (transcoder) transcoder.kill();
    if (dispatcher) dispatcher.destroy('end');
    this.streams.delete(stream);
  }

  destroyAllStreams(except) {
    for (const stream of this.streams.keys()) {
      if (except === stream) continue;
      if (except === true && this.streams.get(stream) === this.streams.last()) continue;
      this.destroyStream(stream);
    }
  }

  playUnknownStream(stream, { seek = 0, volume = 1, passes = 1 } = {}) {
    OpusEncoders.guaranteeOpusEngine();
    const options = { seek, volume, passes };
    const transcoder = this.prism.transcode({
      type: 'ffmpeg',
      media: stream,
      ffmpegArguments: ffmpegArguments.concat(['-ss', String(seek)]),
    });
    this.streams.set(transcoder.output, { transcoder, input: stream });
    transcoder.on('error', e => {
      this.destroyStream(stream);
      if (this.listenerCount('error') > 0) this.emit('error', e);
      this.emit('warn', `prism transcoder error - ${e}`);
    });
    return this.playPCMStream(transcoder.output, options);
  }

  playPCMStream(stream, { seek = 0, volume = 1, passes = 1 } = {}) {
    OpusEncoders.guaranteeOpusEngine();
    const options = { seek, volume, passes };
    this.destroyAllStreams(stream);
    const dispatcher = this.createDispatcher(stream, options);
    if (!this.streams.has(stream)) this.streams.set(stream, { dispatcher, input: stream });
    this.streams.get(stream).dispatcher = dispatcher;
    return dispatcher;
  }

  playOpusStream(stream, { seek = 0, passes = 1 } = {}) {
    const options = { seek, passes, opus: true };
    this.destroyAllStreams(stream);
    const dispatcher = this.createDispatcher(stream, options);
    this.streams.set(stream, { dispatcher, input: stream });
    return dispatcher;
  }

  playBroadcast(broadcast, { volume = 1, passes = 1 } = {}) {
    const options = { volume, passes };
    this.destroyAllStreams();
    const dispatcher = this.createDispatcher(broadcast, options);
    this.streams.set(broadcast, { dispatcher, input: broadcast });
    broadcast.registerDispatcher(dispatcher);
    return dispatcher;
  }

  createDispatcher(stream, options) {
    const dispatcher = new StreamDispatcher(this, stream, options);
    dispatcher.on('end', () => this.destroyStream(stream));
    dispatcher.on('error', () => this.destroyStream(stream));
    dispatcher.on('speaking', value => this.voiceConnection.setSpeaking(value));
    return dispatcher;
  }
}

module.exports = AudioPlayer;

@@ -1258,24 +1258,25 @@
 %0A    return 
+(
 this.streams
@@ -1282,16 +1282,23 @@
 s.last()
+ %7C%7C %7B%7D)
 .transco
@@ -1303,24 +1303,179 @@
 coder;%0A  %7D%0A%0A
+  /**%0A   * The current dispatcher%0A   * @type %7B?StreamDispatcher%7D%0A   */%0A  get currentDispatcher() %7B%0A    return (this.streams.last() %7C%7C %7B%7D).dispatcher;%0A  %7D%0A%0A
   destroy() 

Add AudioPlayer.currentDispatcher