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.
259 lines
7.2 KiB
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;
|
|
}
|
|
|
|
}
|
|
|