/** * @return A copy of this configuration. */ public AudioConfiguration copy() { AudioConfiguration copy = new AudioConfiguration(); copy.setResamplingQuality(resamplingQuality); copy.setOpusEncodingQuality(opusEncodingQuality); copy.setOutputFormat(outputFormat); copy.setFilterHotSwapEnabled(filterHotSwapEnabled); copy.setFrameBufferFactory(frameBufferFactory); return copy; }
/** * @param track Audio track to play * @param configuration Configuration for audio processing * @param remoteNodeManager Manager of remote nodes * @param volumeLevel Mutable volume level */ public RemoteAudioTrackExecutor(AudioTrack track, AudioConfiguration configuration, RemoteNodeManager remoteNodeManager, AtomicInteger volumeLevel) { this.track = track; this.configuration = configuration.copy(); this.remoteNodeManager = remoteNodeManager; this.volumeLevel = volumeLevel; this.executorId = System.nanoTime(); this.frameBuffer = configuration.getFrameBufferFactory().create(BUFFER_DURATION_MS, configuration.getOutputFormat(), null); }
/** * @param configuration Audio encoding or filtering related configuration * @param frameBuffer Frame buffer for the produced audio frames * @param playerOptions State of the audio player. * @param outputFormat Output format to use throughout this processing cycle */ public AudioProcessingContext(AudioConfiguration configuration, AudioFrameBuffer frameBuffer, AudioPlayerOptions playerOptions, AudioDataFormat outputFormat) { this.configuration = configuration; this.frameBuffer = frameBuffer; this.playerOptions = playerOptions; this.outputFormat = outputFormat; this.filterHotSwapEnabled = configuration.isFilterHotSwapEnabled(); } }
/** * @param audioTrack The audio track that this executor executes * @param configuration Configuration to use for audio processing * @param playerOptions Mutable player options (for example volume). * @param useSeekGhosting Whether to keep providing old frames continuing from the previous position during a seek * until frames from the new position arrive. * @param bufferDuration The size of the frame buffer in milliseconds */ public LocalAudioTrackExecutor(InternalAudioTrack audioTrack, AudioConfiguration configuration, AudioPlayerOptions playerOptions, boolean useSeekGhosting, int bufferDuration) { this.audioTrack = audioTrack; AudioDataFormat currentFormat = configuration.getOutputFormat(); this.frameBuffer = configuration.getFrameBufferFactory().create(bufferDuration, currentFormat, queuedStop); this.processingContext = new AudioProcessingContext(configuration, frameBuffer, playerOptions, currentFormat); this.useSeekGhosting = useSeekGhosting; }
@Override public TrackStartRequestMessage decode(DataInput in, int version) throws IOException { long executorId = in.readLong(); AudioTrackInfo trackInfo = new AudioTrackInfo(in.readUTF(), in.readUTF(), in.readLong(), in.readUTF(), in.readBoolean(), null); byte[] encodedTrack = new byte[in.readInt()]; in.readFully(encodedTrack); int volume = in.readInt(); AudioConfiguration configuration = new AudioConfiguration(); configuration.setResamplingQuality(AudioConfiguration.ResamplingQuality.valueOf(in.readUTF())); configuration.setOpusEncodingQuality(in.readInt()); if (version >= VERSION_WITH_FORMAT) { AudioDataFormat format = createFormat(in.readInt(), in.readInt(), in.readInt(), in.readUTF()); configuration.setOutputFormat(format); } long position = 0; if (version >= VERSION_WITH_POSITION) { position = in.readLong(); } return new TrackStartRequestMessage(executorId, trackInfo, encodedTrack, volume, configuration, position); }
public static void init() { AudioSourceManagers.registerRemoteSources(playerManager); playerManager.getConfiguration().setResamplingQuality(AudioConfiguration.ResamplingQuality.HIGH); playerManager.getConfiguration().setOpusEncodingQuality(AudioConfiguration.OPUS_QUALITY_MAX); }
@Override public void encode(DataOutput out, TrackStartRequestMessage message) throws IOException { int version = version(message); out.writeLong(message.executorId); out.writeUTF(message.trackInfo.title); out.writeUTF(message.trackInfo.author); out.writeLong(message.trackInfo.length); out.writeUTF(message.trackInfo.identifier); out.writeBoolean(message.trackInfo.isStream); out.writeInt(message.encodedTrack.length); out.write(message.encodedTrack); out.writeInt(message.volume); out.writeUTF(message.configuration.getResamplingQuality().name()); out.writeInt(message.configuration.getOpusEncodingQuality()); if (version >= VERSION_WITH_FORMAT) { AudioDataFormat format = message.configuration.getOutputFormat(); out.writeInt(format.channelCount); out.writeInt(format.sampleRate); out.writeInt(format.chunkSampleCount); out.writeUTF(format.codecName()); } if (version >= VERSION_WITH_POSITION) { out.writeLong(message.position); } }
@Override public int version(RemoteMessage message) { // Backwards compatibility with older nodes. if (message instanceof TrackStartRequestMessage) { if (((TrackStartRequestMessage) message).position != 0) { return VERSION_WITH_POSITION; } AudioDataFormat format = ((TrackStartRequestMessage) message).configuration.getOutputFormat(); if (!format.equals(StandardAudioDataFormats.DISCORD_OPUS)) { return VERSION_WITH_FORMAT; } return VERSION_INITIAL; } return VERSION_WITH_POSITION; }
/** * @param configuration Audio configuration used for configuring the encoder * @param format Target audio format. */ public OpusChunkEncoder(AudioConfiguration configuration, AudioDataFormat format) { encodedBuffer = ByteBuffer.allocateDirect(format.maximumChunkSize()); encoder = new OpusEncoder(format.sampleRate, format.channelCount, configuration.getOpusEncodingQuality()); this.format = format; }
/** * @param configuration Configuration to use * @param channels Number of channels in input data * @param downstream Next filter in chain * @param sourceRate Source sample rate * @param targetRate Target sample rate */ public ResamplingPcmAudioFilter(AudioConfiguration configuration, int channels, FloatPcmAudioFilter downstream, int sourceRate, int targetRate) { this.downstream = downstream; converters = new SampleRateConverter[channels]; outputSegments = new float[channels][]; SampleRateConverter.ResamplingType type = getResamplingType(configuration.getResamplingQuality()); for (int i = 0; i < channels; i++) { outputSegments[i] = new float[BUFFER_SIZE]; converters[i] = new SampleRateConverter(type, 1, sourceRate, targetRate); } }
/** * Create a new instance */ public DefaultAudioPlayerManager() { sourceManagers = new ArrayList<>(); // Executors trackPlaybackExecutorService = new ThreadPoolExecutor(1, Integer.MAX_VALUE, 10, TimeUnit.SECONDS, new SynchronousQueue<>(), new DaemonThreadFactory("playback")); trackInfoExecutorService = ExecutorTools.createEagerlyScalingExecutor(1, DEFAULT_LOADER_POOL_SIZE, TimeUnit.SECONDS.toMillis(30), LOADER_QUEUE_CAPACITY, new DaemonThreadFactory("info-loader")); scheduledExecutorService = Executors.newScheduledThreadPool(1, new DaemonThreadFactory("manager")); orderedInfoExecutor = new OrderedExecutor(trackInfoExecutorService); // Configuration trackStuckThreshold = TimeUnit.MILLISECONDS.toNanos(10000); configuration = new AudioConfiguration(); cleanupThreshold = new AtomicLong(DEFAULT_CLEANUP_THRESHOLD); frameBufferDuration = DEFAULT_FRAME_BUFFER_DURATION; useSeekGhosting = true; // Additional services remoteNodeManager = new RemoteNodeManager(this); garbageCollectionMonitor = new GarbageCollectionMonitor(scheduledExecutorService); lifecycleManager = new AudioPlayerLifecycleManager(scheduledExecutorService, cleanupThreshold); lifecycleManager.initialise(); }
public AudioPlayerManager getPlayerManager() { if (playerManager == null) { playerManager = registerSourceManagers(new DefaultAudioPlayerManager()); playerManager.getConfiguration().setResamplingQuality( AudioConfiguration.ResamplingQuality.valueOf( avaire.getConfig().getString("audio-quality.resampling", "medium").toUpperCase() ) ); playerManager.getConfiguration().setOpusEncodingQuality( avaire.getConfig().getInt("audio-quality.encoding", AudioConfiguration.OPUS_QUALITY_MAX) ); if (LavalinkManager.LavalinkManagerHolder.lavalink.isEnabled()) { playerManager.enableGcMonitoring(); } playerManager.setFrameBufferDuration(1000); playerManager.setItemLoaderThreadPoolSize(500); AudioSourceManagers.registerRemoteSources(playerManager); AudioSourceManagers.registerLocalSource(playerManager); } return playerManager; }
/** * @return The expected timecode of the next frame to receive from the remote node. */ public long getNextInputTimecode() { boolean dataReceived = hasReceivedData; long frameDuration = configuration.getOutputFormat().frameDuration(); if (dataReceived) { Long lastBufferTimecode = frameBuffer.getLastInputTimecode(); if (lastBufferTimecode != null) { return lastBufferTimecode + frameDuration; } } long seekPosition = pendingSeek.get(); if (seekPosition != NO_SEEK) { return seekPosition; } return dataReceived ? lastFrameTimecode.get() + frameDuration : lastFrameTimecode.get(); }
private void handleTrackFrameData(TrackFrameDataMessage message) throws Exception { RemoteAudioTrackExecutor executor = playingTracks.get(message.executorId); if (executor != null) { if (message.seekedPosition >= 0) { executor.clearSeek(message.seekedPosition); } AudioFrameBuffer buffer = executor.getAudioBuffer(); executor.receivedData(); AudioDataFormat format = executor.getConfiguration().getOutputFormat(); for (AudioFrame frame : message.frames) { buffer.consume(new ImmutableAudioFrame(frame.getTimecode(), frame.getData(), frame.getVolume(), format)); } if (message.finished) { buffer.setTerminateOnEmpty(); trackEnded(executor, false); } } }