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

449 lines
11 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 conteneurs.NoteEventList;
import conteneurs.Spectre;
import generictools.Instant;
import generictools.Pair;
import gui.PlayerControl;
import gui.ProcessSumUp;
import gui.viewer_state.ViewerState;
import processing.buffer.Buffer;
import processing.buffer.NoteBuffer;
import processing.buffer.TemporalBuffer;
import processing.processes.*;
import processing.processes.Process;
import java.util.ArrayList;
/**
* Classe centrale pour le traitement et l'affichage:
*
* Elle est chargé non seulement de lancer, controler et stopper le traitement dans sa globalité,
* mais elle contient aussi le thread d'affichage qui déclenche, au maximum 10 fois par secondes
* la mise à jour des graphs quand leur buffer a été modifié.
*/
public class ProcessControl implements Runnable {
private final ArrayList<Pair<processing.processes.Process, Integer>> processes = new ArrayList<>();
private final ArrayList<Pair<AudioInputToBuffer, Integer>> inputs= new ArrayList<>();
private int mainInput=-1;
private final ArrayList<Buffer> buffers = new ArrayList<>();
private final ArrayList<ViewerState> viewerStates = new ArrayList<>();
private ProcessSumUp processSumUp;
private long timeMs=0;
private final Thread thread = new Thread(this, "GUI Thread");
static private final ProcessControl p = new ProcessControl();
/**
* Retourne l'instance du singleton de ProcessControl
* @return Singleton de ProcessControl
*/
static public ProcessControl instance(){return p;}
private ProcessControl(){
init();
}
/**
* Initialise les processus
*/
public void init(){
mainInput = addInput(AudioInputToBuffer.fromMicro());
addProcess(new FFT(getMainInput().getBuffer(), 50));
addProcess(new FreqEdgeDetector((TemporalBuffer<Spectre>)processes.get(0).first().output(),15,12));
addProcess(new FreqEdgeToEvent((TemporalBuffer<Spectre>)processes.get(1).first().output(), 7, .97f));
addProcess(new EventToNote((TemporalBuffer<NoteEventList>)processes.get(2).first().output()));
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
}
/**
* Démarre le controleur: lance le thread d'interface et ceux des différents process et input...
*/
public void start(){
for(Pair<Process,Integer> p: processes)
p.first().start();
record();
}
/**
* Lance l'enregistrement seul: l'état des process n'est pas modifié, seul les inputs sont démarrés
*/
public void record(){
for(Pair<AudioInputToBuffer,Integer> b: inputs)
b.first().start();
}
/**
* Mets en pause les process et les inputs
*/
public void pause(){
for(Pair<Process,Integer> p: processes)
p.first().pause();
for(Pair<AudioInputToBuffer,Integer> b: inputs)
b.first().pause();
}
/**
* Stoppe l'enregistrement seul: l'état des process n'est pas modifié, seul les inputs sont mis en pause
*/
public void stopRecord(){
for(Pair<AudioInputToBuffer,Integer> b: inputs)
b.first().interrupt();
}
/**
* Stoppe tout les les process et les inputs
*/
public void stop(){
stopRecord();
for(Pair<Process,Integer> p: processes)
p.first().interrupt();
}
/**
* Tue tous les process, les inputs et le thread d'interface graphique
*/
public void kill(){
stop();
PostProcessScript.interrupt();
PlayerControl.instance().stop();
thread.interrupt();
}
/**
* Réinitialise le controleur à sont état initial
*/
public void reset(){
stop();
/*
inputs.clear();
processes.clear();
buffers.clear();
viewerStates.clear();
init();
PlayerControl.instance().setBuffer(getMainInput().getBuffer());
*/
for (Buffer b: buffers)
b.clear();
PlayerControl.instance().setFrame(Instant.fromIndex(0,44100));
}
/**
* Démarre le post-processing (l'exportation vers lilypond
*/
public void launchPostProcess(){
System.out.println("POST PROCESS!!!!!");
stop();
PostProcessScript.launchScript((NoteBuffer) processes.get(3).first().output(), new Runnable() {
@Override
public void run() {
System.out.println("PostProcess finished");
}
});
}
/**
* @return Vrai si il existe au moins un process ou input qui est toujours en traitement
*/
public boolean isRunning(){
for(Pair<Process,Integer> p: processes)
if(!p.first().isRunning())
return false;
for(Pair<AudioInputToBuffer,Integer> b: inputs)
if(!b.first().isRunning())
return false;
return true;
}
/**
* @return Vrai si tout les process sont en attente: les inputs bloquent le traitement
*/
public boolean isWaiting(){
for(Pair<Process,Integer> p: processes)
if(!p.first().isWaiting())
return false;
return true;
}
/**
* Renvoie un process utilisé
* @param id identifiant du process à récuperer
* @return le process si il a été trouvé, null sinon.
*/
public Process<?> getProcess(int id){
if(id>=processes.size()||id<0)
return null;
return processes.get(id).first();
}
/**
* Renvoie le process portant le nom en argument
* @param name nom du process à récuperer
* @return le process si il a été trouvé, null sinon.
*/
public Process getProcess(String name){
for(Pair<Process,Integer> p:processes){
if(p.first().name().equals(name))
return p.first();
}
return null;
}
/**
* Renvoie le nombre de processus
* @return
*/
public int getProcessNbr(){
return processes.size();
}
/**
* Renvoie l'index du buffer associé à un process
* @param processID Index correspondant au process
* @return L"index du buffer
*/
public Integer getProcessBufferID(int processID) {
if(processID>=processes.size()||processID<0)
return null;
return processes.get(processID).second();
}
/**
* Renvoie un input
* @param id identifiant de l'input à récuperer
* @return l'input si il a été trouvé, null sinon.
*/
public AudioInputToBuffer getInput(int id){
if(id>=inputs.size()||id<0)
return null;
return inputs.get(id).first();
}
/**
* Renvoie le nombre d'inputs
* @return
*/
public int getInputNbr() {
return inputs.size();
}
/**
* Renvoie l'index du buffer associé à un input
* @param inputID Index correspondant à l'input
* @return L"index du buffer
*/
public Integer getInputBufferID(int inputID) {
if(inputID>=inputs.size()||inputID<0)
return null;
return inputs.get(inputID).second();
}
/**
* Renvoie l'input principal
* @return
*/
public AudioInputToBuffer getMainInput() {
return inputs.get(mainInput).first();
}
public int getMainInputID(){
return mainInput;
}
public int getBufferNbr(){
return buffers.size();
}
/**
* Renvoie un buffer
* @param id identifiant du buffer à récuperer
* @return le buffer si il a été trouvé, null sinon.
*/
public Buffer getBuffer(int id){
if(id>=buffers.size()||id<0)
return null;
return buffers.get(id);
}
/**
* Renvoie l'index d'un buffer
* @param b buffer à chercher
* @return index de la première occurence ( et normalement la seule) du buffer dans la liste
*/
public int getBufferIndex(Buffer b){
return buffers.indexOf(b);
}
/**
* Ajout d'un process à la liste des processus, son buffer de sortie est enregistrer et ajouté à la liste des buffers
* @param p Le process à ajouter
* @return l'index sur lequel il a été ajouté
*/
int addProcess(Process p){
for(Pair<Process,Integer> i:processes)
if(i.first()==p)
return -1;
processes.add(new Pair<>(p, addBuffer(p.output())));
updateProcessesView();
return processes.size()-1;
}
/**
* Ajout d'un input à la liste des processus, son buffer de sortie est enregistrer et ajouté à la liste des buffers
* @param p L'input à ajouter
* @return l'index sur lequel il a été ajouté
*/
int addInput(AudioInputToBuffer p){
for(Pair<AudioInputToBuffer,Integer> i:inputs)
if(i.first()==p)
return -1;
inputs.add(new Pair<>(p, addBuffer(p.getBuffer())) );
updateProcessesView();
return inputs.size()-1;
}
private int addBuffer(Buffer b){
buffers.add(b);
return buffers.size()-1;
}
/**
* Modifie une input, en reconnectant les vues et les process qui utilisaient la précédante
* @param inputID index de l'input à remplacer
* @param newInput nouvelle input
* @return Vrai si l'opération c'est bien déroulée
*/
public boolean setInput(int inputID, AudioInputToBuffer newInput){
if(newInput==null)
return false;
for(Pair<AudioInputToBuffer,Integer> i:inputs)
if(i.first()==newInput)
return false;
if(inputID>= inputs.size())
return false;
boolean inputStarted = inputs.get(inputID).first().isAlive();
inputs.get(inputID).first().interrupt();
newInput.setBuffer(inputs.get(inputID).first().getBuffer());
inputs.get(inputID).setFirst(newInput);
updateProcessesView();
return true;
}
/**
* Enregistre la fenetre récapitulative des traitements pour pouvoir déclancher sa mise à jour
* @param pV fenêtre récapitulative
*/
public void setProcessesView(ProcessSumUp pV) {
this.processSumUp = pV;
processSumUp.updateView();
}
/**
* Déclanche la mise à jour de la fenêtre récapitulative des traitements
*/
private void updateProcessesView(){
if(processSumUp !=null)
processSumUp.updateView();
}
/**
* Enregistre un viewerState pour qu'il soit pris en compte dans le thread d'interface
* @param viewerState ViewerState à enregistrer
* @return Vrai si il n'était pas déjà ajouté
*/
public boolean registerViewerState(ViewerState viewerState){
if(viewerStates.contains(viewerState))
return false;
viewerStates.add(viewerState);
viewerState.invalidate();
return true;
}
public void run() {
while (!Thread.interrupted()) {
for (ViewerState b : viewerStates)
if (b.isVisible())
b.applyState();
if (processSumUp != null) {
timeMs = System.currentTimeMillis() - timeMs;
processSumUp.update(1f / timeMs * 1000f);
timeMs = System.currentTimeMillis();
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
return;
}
}
}
}