reversound/src/processing/BufferPlayer.java
2014-06-16 16:48:34 +02:00

222 lines
4.9 KiB
Java

/*
Reversound is used to get the music sheet of a piece from a music file.
Copyright (C) 2014 Gabriel AUGENDRE
Copyright (C) 2014 Gabriel DIENY
Copyright (C) 2014 Arthur GAUCHER
Copyright (C) 2014 Gabriel LEPETIT-AIMON
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package processing;
import generictools.Instant;
import gui.PlayerControl;
import processing.buffer.StorageIterator;
import processing.buffer.TemporalBuffer;
import javax.sound.sampled.*;
/**
* Created by gaby on 16/04/14.
*/
public class BufferPlayer implements Runnable{
TemporalBuffer<Float> buffer;
SourceDataLine line;
int currentFrame = 0;
StorageIterator<Float> currentSample;
boolean muted = false;
float volume = 1;
int iTest = 0;
Thread thread = new Thread(this);
boolean running = false;
final float CLOCK_PERIOD = .05f;
public BufferPlayer(TemporalBuffer<Float> buffer) {
this.buffer = buffer;
DataLine.Info info = new DataLine.Info(SourceDataLine.class, new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, buffer.getSampleRate(),16,1,2,44100, false), (int)(CLOCK_PERIOD*44100*2));
Mixer m = AudioSystem.getMixer(AudioSystem.getMixerInfo()[0]);
for(Line.Info i : m.getSourceLineInfo()){
System.out.println(i);
}
try {
line = (SourceDataLine)AudioSystem.getLine(info);
line.open();
} catch (LineUnavailableException e) {
e.printStackTrace();
}
currentSample = buffer.getIterator();
}
/**
* Démarre le lecteur de buffer
* @return Renvoie vrai si il n'était pas lancé
*/
public boolean play(){
if(thread.isAlive())
return false;
line.flush();
if(!line.isActive())
line.start();
thread = new Thread(this, "BufferPlayer");
thread.setPriority(Thread.MAX_PRIORITY);
running = true;
thread.start();
return true;
}
/**
* Mets en pause le lecteur
* @return
*/
public boolean pause(){
running = false;
thread.interrupt();
return true;
}
/**
* Modifie la frame actuelle
* @param frame
*/
public void setFrame(Instant frame){
currentFrame = frame.mapToIndex(buffer);
currentSample = buffer.getIterator(frame.mapToIndex(buffer));
PlayerControl.instance().frameChanged(frame);
}
private void clean(){
running = false;
line.stop();
}
public static byte [] float2ByteArray (float value)
{
int v = Math.round(value);
return new byte[]{
(byte) (v & 0xFF),
(byte) ((v >>> 8) & 0xFF)
};
}
public Instant getCurrentFrame(){
return Instant.fromIndex(currentFrame, buffer);
}
/**
* Retourne l'état muet du lecteur
* @return Renvoie vrai si muet
*/
public boolean isMuted() {
return muted;
}
/**
* Modifie l'état muet du lecteur
* @param muted
*/
public void setMuted(boolean muted) {
this.muted = muted;
}
public float getVolume() {
return volume;
}
public void setVolume(float volume) {
if(volume==0)
setMuted(true);
else
setMuted(false);
this.volume = volume;
}
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see Thread#run()
*/
@Override
public void run() {
boolean catchBack = false;
while(!thread.interrupted()&&running) {
long time = System.currentTimeMillis();
if (currentFrame >= ProcessControl.instance().getMainInput().getBuffer().size()) {
PlayerControl.instance().stop();
return;
}
int lastI = (int) (buffer.getSampleRate() * CLOCK_PERIOD);
float k = 1;
if (!muted && currentSample.hasNext()) {
if (catchBack) {
k = 1.5f;
}
for (int i = 0; i < lastI*k; i++) {
if (currentSample.hasNext())
line.write(float2ByteArray(currentSample.next() * volume), 0, 2);
else {
byte[] nullBytes = new byte[(int)(lastI*k- i) * 2];
line.write(nullBytes, 0, nullBytes.length);
break;
}
}
}
currentFrame += lastI*k;
PlayerControl.instance().frameChanged(Instant.fromIndex(currentFrame, buffer));
time-=System.currentTimeMillis();
try {
if((int)(CLOCK_PERIOD*1000)+time>0) {
thread.sleep((int) (CLOCK_PERIOD * 1000) + time);
catchBack = false;
}
else {
thread.sleep(0);
catchBack = true;
}
} catch (InterruptedException e) {
clean();
return;
}
}
clean();
}
}