/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package moe.nekojimi.chords; import java.io.*; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; import javax.sound.sampled.*; import net.dv8tion.jda.api.audio.AudioSendHandler; /** * * @author jimj316 */ public class MusicHandler implements AudioSendHandler, Closeable, Consumer { // private QueueThing queueManager; private QueueManager queueManager; // private final LinkedList trackQueue = new LinkedList<>(); // private final Queue queue = new ConcurrentLinkedQueue<>(); // private final CircularByteBuffer audioBuffer = new CircularByteBuffer(3840 * 1024); private boolean shouldPlay = true; private int byteCount; private boolean arrayErr = false; private Consumer nowPlayingConsumer; public void setNowPlayingConsumer(Consumer nowPlayingConsumer) { this.nowPlayingConsumer = nowPlayingConsumer; } private Track currentTrack; // private TrackPlayer player; private final List playingTracks = new ArrayList<>(); public MusicHandler() { } @Override public void accept(Track t) { play(t); } void setQueueManager(QueueManager manager) { queueManager = manager; } public void playOver(Track track) { } public boolean play(Track track) { return play(track, false); } public boolean play(Track track, boolean immediate) { if (track == currentTrack) return false; if (immediate) { System.out.println("Immediate next - clearing buffer"); playingTracks.clear(); } try { if (currentTrack != null) { if (!currentTrack.isKept()) currentTrack.delete(); currentTrack = null; } currentTrack = track; if (nowPlayingConsumer != null) nowPlayingConsumer.accept(currentTrack); if (currentTrack == null) { return false; } // System.out.println("Playing track " + currentTrack.getLocation().getAbsolutePath()); arrayErr = false; byteCount = 3840; TrackPlayer player = new TrackPlayer(currentTrack); playingTracks.add(player); // System.out.println("Queue filled to " + audioBuffer.getCurrentNumberOfBytes()); return true; } catch (UnsupportedAudioFileException | IOException ex) { Logger.getLogger(Chords.class.getName()).log(Level.SEVERE, null, ex); currentTrack = null; requestTrack(); } finally { } return false; } public boolean requestTrack() { if (isPlaying()) System.out.println("How did we get here?"); System.out.println("MusicHandler requesting track..."); List> request = queueManager.request(1, this); // Queuemanager will syncronously attempt to call play() return !playingTracks.isEmpty(); } public boolean isPlaying() { return !playingTracks.isEmpty(); } public boolean isShouldPlay() { return shouldPlay; } public void setShouldPlay(boolean shouldPlay) { if (!this.shouldPlay && shouldPlay) requestTrack(); this.shouldPlay = shouldPlay; } @Override public boolean canProvide() { if (playingTracks.isEmpty()) return false; for (TrackPlayer player : playingTracks) if (player.has(1)) return true; return false; // If we have something in our buffer we can provide it to the send system // return audioBuffer.getCurrentNumberOfBytes() > byteCount && shouldPlay; } @Override public ByteBuffer provide20MsAudio() { ByteBuffer ret = ByteBuffer.allocate(byteCount); while (ret.position() < byteCount && !playingTracks.isEmpty()) { boolean outOfInput = true; List mixes = new ArrayList<>(); List emptyPlayers = new ArrayList<>(); for (TrackPlayer player : playingTracks) { try { ByteBuffer read = player.read(ret.remaining()); if (ret.limit() + read.position() >= byteCount) outOfInput = false; mixes.add(read); // ret.put(read); } catch (TrackPlayer.OutOfInputException | IOException ex) { // System.out.println("Track player " + player + " stopped giving input: " + ex.getMessage()); emptyPlayers.add(player); // System.out.println("Track ended, starting next."); // outOfInput = true; } } playingTracks.removeAll(emptyPlayers); ret.put(mixBuffers(mixes)); if (outOfInput) { boolean foundNext = requestTrack(); if (!foundNext) break; } } // System.out.println("Buffer filled, submitting."); ret.rewind(); // required apparently, if returned buf has pos > 0 you get silence assert ret.hasArray(); // output MUST be array backed return ret; } public Track getCurrentTrack() { return currentTrack; } @Override public boolean isOpus() { return false; //To change body of generated methods, choose Tools | Templates. } @Override public void close() throws IOException { } private ByteBuffer mixBuffers(List mixes) { // System.out.println("Mixing " + mixes.size() + " buffers"); if (mixes.size() == 1) return mixes.get(0); int maxSize = 0; for (ByteBuffer buf : mixes) { if (buf.limit() > maxSize) maxSize = buf.position(); } ByteBuffer ret = ByteBuffer.allocate(maxSize); for (int i = 0; i < ret.limit(); i++) { int byteTotal = 0; int mixCount = 0; for (ByteBuffer buf : mixes) { if (i < buf.limit()) { byteTotal += buf.get(i); mixCount++; } } double avg = ((double) byteTotal) / mixCount; byte byteVal = (byte) Math.round(avg); ret.put(byteVal); } ret.rewind(); return ret; } public boolean skipTrack() { if (!isPlaying()) return false; playingTracks.clear(); currentTrack = null; requestTrack(); return true; } }