/* 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 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> processes = new ArrayList<>(); private final ArrayList> inputs= new ArrayList<>(); private int mainInput=-1; private final ArrayList buffers = new ArrayList<>(); private final ArrayList 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)processes.get(0).first().output(),15,12)); addProcess(new FreqEdgeToEvent((TemporalBuffer)processes.get(1).first().output(), 7, .97f)); addProcess(new EventToNote((TemporalBuffer)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 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 b: inputs) b.first().start(); } /** * Mets en pause les process et les inputs */ public void pause(){ for(Pair p: processes) p.first().pause(); for(Pair 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 b: inputs) b.first().interrupt(); } /** * Stoppe tout les les process et les inputs */ public void stop(){ stopRecord(); for(Pair 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 p: processes) if(!p.first().isRunning()) return false; for(Pair 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 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 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 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 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 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; } } } }