2014-06-16 16:09:55 +02:00
|
|
|
/*
|
|
|
|
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/>.
|
2014-06-16 16:48:34 +02:00
|
|
|
*/
|
|
|
|
package processing;
|
2014-06-11 15:35:06 +02:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|