449 lines
11 KiB
Java
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;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|