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.processes;
|
2014-06-11 15:35:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2014-06-16 16:48:34 +02:00
|
|
|
}
|