/* 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 . */ 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 buffer; SourceDataLine line; int currentFrame = 0; StorageIterator 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 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 Runnable is used * to create a thread, starting the thread causes the object's * run method to be called in that separately executing * thread. *

* The general contract of the method run 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(); } }