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

515 lines
12 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.processes;
import generictools.Pair;
import processing.buffer.Buffer;
import processing.buffer.BufferEvent;
import processing.properties.PropertiesManager;
import javax.swing.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.stream.Collectors;
public abstract class Process<T> implements Runnable {
final Buffer[] inputs;
private boolean[] inputsInterupted;
final Buffer<T> output;
private boolean interuptAfter = false, paused = false, waiting = true;
private final String name;
private PropertiesManager properties;
private ArrayList<Listener> listeners = new ArrayList<>();
private LinkedList<Pair<BufferEvent,Integer> > bufferEvents;
int updateID = 0;
private Thread thread;
private BufferEventProcessor bProcessor;
private boolean boxDisplayed = false;
public enum ProcessingOrder {
NO_ORDER,
ASCENDING_ORDER
}
public enum ProcessingState {
PROCESSING,
WAITING,
PAUSED,
STOPPED
}
public Process(String name, Buffer[] inputs, Buffer<T> output) {
this.inputs = inputs;
this.output = output;
this.name = name;
init();
}
public Process(String name, Buffer input, Buffer<T> output) {
inputs = new Buffer[1];
inputs[0] = input;
this.output = output;
this.name = name;
init();
}
public void init(){
bufferEvents = new LinkedList<>();
thread = new Thread(this);
output.setName(name);
output.setType(getType());
switch (getProcessingOrder()){
case ASCENDING_ORDER:
output.setProcessingOrder(ProcessingOrder.ASCENDING_ORDER);
for (int i = 0; i < inputs.length; i++) {
if(inputs[i].getProcessingOrder()!=ProcessingOrder.ASCENDING_ORDER) {
output.setProcessingOrder(ProcessingOrder.NO_ORDER);
break;
}
}
break;
}
inputsInterupted = new boolean[inputs.length];
for(int i=0; i<inputs.length; i++) {
inputs[i].connectProcess(this);
inputsInterupted[i] = false;
}
properties = new PropertiesManager();
bProcessor = initBufferEventProcessor();
}
/**
* Renvoie les buffers d'entrées
* @return Liste des buffers d'entrées
*/
public Buffer[] inputs() {
return inputs;
}
/**
* Renvoie le premier (possiblement le seul) buffers d'entrées
* @return Le premier buffers de la liste des buffers d'entrées
*/
public Buffer input(){return inputs[0];}
/**
* Renvoie le buffer de sortie
* @return renvoie le buffer de sortie
*/
public Buffer<T> output() {
return output;
}
public String name(){
return name;
}
public void setInput(int inputID, Buffer newInput){
inputs[inputID].disconnectProcess(this);
newInput.connectProcess(this);
inputs[inputID] = newInput;
}
public void addBufferEvent(BufferEvent e){
if(e.getType()== BufferEvent.Type.SAMPLE_ADDED) {
bufferEvents.addLast(new Pair<>(e, updateID));
emitProcessEvent(ProcessEvent.Type.PROGRESS_CHANGED);
}else {
if (e.getType() == BufferEvent.Type.INTERRUPT_PROCESS) {
int bufferID = 0;
for (int i = 1; i < inputs.length; i++) {
if (inputs[i] == e.getSource())
bufferID = i;
}
inputsInterupted[bufferID] = true;
if (isAllInputInterrupted()) {
interuptAfter = true;
emitProcessEvent(ProcessEvent.Type.INTERRUPT_AFTER);
}
} else if (e.getType() == BufferEvent.Type.INVALIDATE) {
boolean b = pause();
bufferEvents.clear();
if (b)
start();
} else {
for (int i = 0; i < bufferEvents.size(); i++)
if (bufferEvents.get(i).first().getSource() == e.getSource()) {
bufferEvents.remove(i);
break;
}
bufferEvents.addFirst(new Pair<>(e, updateID));
emitProcessEvent(ProcessEvent.Type.PROGRESS_CHANGED);
}
}
}
public void run() {
while (true) {
propertyManager().lockProperty();
while (!bufferEvents.isEmpty()&&!paused) {
setWaiting(false);
Pair<BufferEvent, Integer> event = bufferEvents.pollFirst();
if(event==null)
continue;
bProcessor.processBufferEvent(event.first(), event.second());
emitProcessEvent(ProcessEvent.Type.PROGRESS_CHANGED);
try {
Thread.sleep(0);
} catch (InterruptedException e) {
clean();
return;
}
}
setWaiting(true);
if(interuptAfter){
clean();
return;
}
try {
propertyManager().unlockProperty();
Thread.sleep(50);
propertyManager().lockProperty();
} catch (InterruptedException e) {
clean();
return;
}
while(paused)
try {
Thread.sleep(500);
} catch (InterruptedException e) {
clean();
return;
}
}
}
protected void clean(){
properties.unlockProperty();
pause();
output.lastSampleAdded();
}
/**
* Démarre le traitement
* @return Renvoie vrai si le traitement n'était pas déjà démarré
*/
public boolean start(){
if(thread.isAlive()) {
if(paused) {
paused = false;
return true;
}
return false;
}
interuptAfter = false;
paused = false;
thread = new Thread(this);
thread.setName(name);
thread.start();
emitProcessEvent(ProcessEvent.Type.PAUSED_STATE_CHANGED);
return true;
}
/**
* Interompt le traitement
* @return Renvoie vrai si le traitement était lancé
*/
public boolean interrupt(){
if(!thread.isAlive())
return false;
thread.interrupt();
emitProcessEvent(ProcessEvent.Type.PAUSED_STATE_CHANGED);
return true;
}
/**
* Interompt le traitement une fois que tout les événements ont été traités
* @return Renvoie vrai si le traitement était lancé
*/
public boolean stopAfterEventsClean(){
if(!thread.isAlive())
return false;
emitProcessEvent(ProcessEvent.Type.INTERRUPT_AFTER);
interuptAfter = true;
return true;
}
/**
* Mais en pause le traitement sans supprimer le thread
* @return Renvoie vrai si le traitement était lancé
*/
public boolean pause(){
if(!thread.isAlive())
return false;
paused = true;
emitProcessEvent(ProcessEvent.Type.PAUSED_STATE_CHANGED);
return true;
}
public void recomputeAll(){
output.clear();
interrupt();
properties.unlockProperty();
bufferEvents.clear();
bProcessor = initBufferEventProcessor();
for(Buffer input: inputs()){
ArrayList<Integer> indexes = input.getSamplesIndex();
for(Integer index:indexes)
addBufferEvent(new BufferEvent(BufferEvent.Type.SAMPLE_ADDED, index, input));
}
start();
for(Process p:output().getUsedBy())
p.recomputeAll();
}
public boolean isAllInputInterrupted(){
for (boolean anInputsInterupted : inputsInterupted) {
if (!anInputsInterupted)
return false;
}
return true;
}
private void setWaiting(boolean waiting){
if(this.waiting == waiting)
return;
this.waiting = waiting;
emitProcessEvent(ProcessEvent.Type.WAITING_STATE);
}
/**
* Renvoie le nom du processus
* @return Le nom
*/
public String getName() {
return name;
}
/**
* Renvoie le buffer de sortie
* @return buffer
*/
public Buffer<T> getOutput() {
return output;
}
/**
* Renvoie un buffer d'entrée
* @param index index du buffer d'entrée
* @return Le buffer correspondant /!\ Peut renvoyer null si l'index n'est pas correct /!\
*/
public Buffer getInputs(int index) {
if(index>=inputs.length || index<1)
return null;
return inputs[index];
}
/**
* Renvoie le nombre de buffer d'entrée
* @return Nombre d'inputs
*/
public int getInputsNbr(){
return inputs.length;
}
/**
* Renvoie la liste des buffersEvent non traités
* @return liste des buffersEvent
*/
public ArrayList<BufferEvent> getBufferEventsQueue() {
ArrayList<BufferEvent> r = new ArrayList<>(bufferEvents.size());
r.addAll(bufferEvents.stream().map(Pair<BufferEvent, Integer>::first).collect(Collectors.toList()));
return r;
}
/**
* Renvoie les propriétés associées à cet algorythme
* @return le propertiesManager du process
*/
public PropertiesManager propertyManager() {
return properties;
}
/**
* Renvoie l'état du processus
* @return Vrai si tout l'algo est en attente d'événement sur les canaux d'entrées
*/
public boolean isWaiting() {
return waiting;
}
/**
* Renvoie l'état du processus
* @return Vrai si l'algo est en pause
*/
public boolean isPaused() {
return paused;
}
/**
* Renvoie l'état du processus
* @return Vrai si le processus est lancé
*/
public boolean isRunning(){
return thread.isAlive();
}
/**
* Renvoie la valeur de la progression du process
* @return la progression: 0 -> aucun échantillon traité; 1 -> tous les échantillons ont été traités
*/
public float getProgress(){
if(bufferEvents.size() == 0)
return 0;
double n = getSampleNbrPerIteration();
return 1-(float)Math.log(n)/(float)Math.log(bufferEvents.size()+n-1);
}
protected float getSampleNbrPerIteration(){return 10;}
/**
* Renvoie le type du buffer de sortie
* @return La class des échantillones du buffer de sortie
*/
protected abstract Class getType();
protected abstract ProcessingOrder getProcessingOrder();
protected interface BufferEventProcessor {
void processBufferEvent(BufferEvent e, int updateID);
}
protected abstract BufferEventProcessor initBufferEventProcessor();
public interface Listener{
public void processEvent(ProcessEvent event);
}
/**
* Connecte ce process à un listener qu'il fournit, de manière à propager les BufferEvent jusqu'à cet objet
* @param p objet à connecter
* @return Vrai si l'opération à fontionner (si le listener n'était pas déjà ajouté)
*/
public boolean addListener(Listener p){
if(listeners.contains(p))
return false;
listeners.add(p);
return true;
}
/**
* Déconnecte ce process d'un listener
* @param p listener à déconnecter
* @return Vrai si l'opération à fontionner (si le listener était dans la liste)
*/
public boolean removeListener(Listener p){
int id = listeners.indexOf(p);
if(id == -1)
return false;
listeners.remove(id);
return true;
}
private void emitProcessEvent(ProcessEvent event) {
SwingUtilities.invokeLater(() -> {
for (Listener p : listeners)
p.processEvent(event);
});
}
private void emitProcessEvent(ProcessEvent.Type type) {
emitProcessEvent(ProcessEvent.eventFrom(type, this));
}
/**
* Indique si la box du buffer est affichée dans le ProcessSumUp ou non.
* @return true si elle est affichée, false sinon.
*/
public boolean isBoxDisplayed() {
return boxDisplayed;
}
/**
* Change l'état d'affichage de la box du Buffer dans le ProcessSumUp. N'appeler que lorsque le système crée la Box.
* @param boxDisplayed L'état : true si on l'affiche, false si on l'enlève.
*/
public void setBoxDisplayed(boolean boxDisplayed) {
this.boxDisplayed = boxDisplayed;
}
}