Discord bot that plays music from every website ever via youtube-dl
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Chords/src/main/java/moe/nekojimi/chords/MusicHandler.java

259 lines
7.2 KiB

/*
* 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<Track>
{
// private QueueThing<?, Track> queueManager;
private QueueManager queueManager;
// private final LinkedList<Track> trackQueue = new LinkedList<>();
// private final Queue<byte[]> queue = new ConcurrentLinkedQueue<>();
// private final CircularByteBuffer audioBuffer = new CircularByteBuffer(3840 * 1024);
private boolean shouldPlay = true;
private int byteCount;
private boolean arrayErr = false;
private Consumer<Track> nowPlayingConsumer;
public void setNowPlayingConsumer(Consumer<Track> nowPlayingConsumer)
{
this.nowPlayingConsumer = nowPlayingConsumer;
}
private Track currentTrack;
// private TrackPlayer player;
private final List<TrackPlayer> 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<QueueThing.Promise<Track, Track>> 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<ByteBuffer> mixes = new ArrayList<>();
List<TrackPlayer> 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<ByteBuffer> 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;
}
}