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/>.
|
|
|
|
|
*/package processing;
|
2014-06-11 15:35:06 +02:00
|
|
|
|
|
|
|
|
|
import conteneurs.Note;
|
|
|
|
|
import generictools.Instant;
|
|
|
|
|
import generictools.Strings;
|
|
|
|
|
import processing.buffer.NoteBuffer;
|
|
|
|
|
import processing.properties.FloatProperty;
|
|
|
|
|
|
|
|
|
|
import javax.swing.*;
|
|
|
|
|
import javax.swing.filechooser.FileNameExtensionFilter;
|
|
|
|
|
import java.io.File;
|
|
|
|
|
import java.io.FileWriter;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.lang.reflect.Array;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
|
|
|
|
|
/**Created by gaby on 03/06/14.
|
|
|
|
|
* Script qui sert à l'exportation vers Lilypond. Regroupe tous les algo de post-traitement nécéssaires à cette exportation. */
|
|
|
|
|
public class PostProcessScript implements Runnable{
|
|
|
|
|
|
|
|
|
|
private NoteBuffer buffer;
|
|
|
|
|
private float tolerance = 0.08f;
|
|
|
|
|
private float progress = 0;
|
|
|
|
|
Runnable callBack;
|
|
|
|
|
|
|
|
|
|
Thread thread = null;
|
|
|
|
|
private static PostProcessScript script = null;
|
|
|
|
|
|
|
|
|
|
private PostProcessScript(NoteBuffer buffer, Runnable callBack){
|
|
|
|
|
this.buffer = buffer;
|
|
|
|
|
this.callBack = callBack;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Lance le script
|
|
|
|
|
* @param buffer
|
|
|
|
|
* @param callBack
|
|
|
|
|
*/
|
|
|
|
|
public static void launchScript(NoteBuffer buffer, Runnable callBack){
|
|
|
|
|
interrupt();
|
|
|
|
|
PostProcessScript script = new PostProcessScript(buffer, callBack);
|
|
|
|
|
script.thread = new Thread(script);
|
|
|
|
|
script.thread.start();
|
|
|
|
|
script = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void interrupt(){
|
|
|
|
|
if(script!=null && script.thread!=null && script.thread.isAlive())
|
|
|
|
|
script.thread.interrupt();
|
|
|
|
|
script = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
unifyTempo();
|
|
|
|
|
|
|
|
|
|
// System.out.println("NOTES:");
|
|
|
|
|
//
|
|
|
|
|
// for (int i = 0; i < buffer.getNotesNbr(); i++) {
|
|
|
|
|
// System.out.println("\t" + buffer.getNote(i).getNoteName() + " (" + buffer.getNote(i).getUnifiedDuration() + ")");
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
JFileChooser jfc = new JFileChooser();
|
|
|
|
|
jfc.setDialogTitle("Choix du dossier de sortie");
|
|
|
|
|
jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
|
|
|
|
int ret = jfc.showOpenDialog(null);
|
|
|
|
|
if(ret != JFileChooser.APPROVE_OPTION)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
File file = new File(jfc.getSelectedFile().getPath()+"/exportLilypond.txt");
|
|
|
|
|
System.out.println(jfc.getSelectedFile().getPath()+"/exportLilypond.txt");
|
|
|
|
|
try {
|
|
|
|
|
if(!file.exists())
|
|
|
|
|
file.createNewFile();
|
|
|
|
|
FileWriter fileWriter = new FileWriter(file);
|
|
|
|
|
fileWriter.write(toLilyPond());
|
|
|
|
|
fileWriter.close();
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(callBack!=null)
|
|
|
|
|
try {
|
|
|
|
|
callBack.run();
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String toLilyPond(){
|
|
|
|
|
String r = "<< {";
|
|
|
|
|
for (int i = 0; i < buffer.getNotesNbr(); i++) {
|
|
|
|
|
r+=buffer.getNote(i).getEnglishNoteName() + buffer.getNote(i).getUnifiedDuration() + " ";
|
|
|
|
|
}
|
|
|
|
|
r+="} >>";
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Cherche le tempo du morceau en se basant sur les écarts entre les notes.
|
|
|
|
|
* A partir de la, il donne a chaque note une duree (ronde, blanche noire, croche, etc) et permet de trouver le rythme */
|
|
|
|
|
private void unifyTempo(){
|
|
|
|
|
ArrayList<Float> listEcarts = calculeEcarts(bufferSortOut(buffer));
|
|
|
|
|
ArrayList<Float> EcartsReference = new ArrayList<>();
|
|
|
|
|
ArrayList<Integer> nEcartParType = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Cr<43>ation des cat<61>gories de r<>f<EFBFBD>rence, tri<72>es par ordre croissant
|
|
|
|
|
|
|
|
|
|
EcartsReference.add(listEcarts.get(0));
|
|
|
|
|
nEcartParType.add(1);
|
|
|
|
|
for (int i = 1; i < listEcarts.size(); i++) {
|
|
|
|
|
float currentEcart = listEcarts.get(i);
|
|
|
|
|
boolean newCategoryNeeded = true;
|
|
|
|
|
for (int j = 0; j < EcartsReference.size(); j++) {
|
|
|
|
|
//Si on est dans la marge de tolerance d<>finie en param<61>tre, on red<65>finit la r<>f<EFBFBD>rence gr<67>ce <20> une moyenne pond<6E>r<EFBFBD>e
|
|
|
|
|
if(currentEcart>((1-tolerance)*EcartsReference.get(j)) && currentEcart<((1+tolerance)*EcartsReference.get(j))){
|
|
|
|
|
EcartsReference.set(j, ( (nEcartParType.get(j)*EcartsReference.get(j))+ currentEcart) / (nEcartParType.get(j)+1) );
|
|
|
|
|
nEcartParType.set(j, nEcartParType.get(j)+1);
|
|
|
|
|
newCategoryNeeded = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//Sinon cr<63>ation d'une nouvelle cat<61>gorie si aucune existante ne convient
|
|
|
|
|
if(newCategoryNeeded){
|
|
|
|
|
int j = 0;
|
|
|
|
|
while(j<EcartsReference.size() && EcartsReference.get(j)<= currentEcart){
|
|
|
|
|
j++;
|
|
|
|
|
}
|
|
|
|
|
EcartsReference.add(j,currentEcart);
|
|
|
|
|
nEcartParType.add(j,1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Definition des ecarts unifies : l'ecart le plus utilise est la noire par défaut sauf si dans ce cas, le plus grand est > à une ronde
|
|
|
|
|
ArrayList<String> UnifiedEcarts = new ArrayList<>(EcartsReference.size());
|
|
|
|
|
int indexEcartMostUsed = 0;
|
|
|
|
|
int maxUsed = 0;
|
|
|
|
|
for (int i = 0; i < nEcartParType.size(); i++) {
|
|
|
|
|
if( nEcartParType.get(i)> maxUsed){
|
|
|
|
|
maxUsed = nEcartParType.get(i);
|
|
|
|
|
indexEcartMostUsed = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < nEcartParType.size(); i++) {
|
|
|
|
|
UnifiedEcarts.add("0");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Cas où on se retrouve avec le max plus grand qu'une ronde
|
|
|
|
|
while(indexEcartMostUsed < EcartsReference.size()-1 && EcartsReference.get(indexEcartMostUsed)*(4+tolerance)>EcartsReference.get(EcartsReference.size()-1)){
|
|
|
|
|
indexEcartMostUsed++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Donne à chaque note une durée unified
|
|
|
|
|
for (int i = 0; i < buffer.getNotesNbr(); i++) {
|
2014-06-14 20:45:14 +02:00
|
|
|
|
double duration = i!=buffer.getNotesNbr()-1?buffer.getNote(i+1).getStart().substract(buffer.getNote(i).getStart()):buffer.getNote(i).getDuration();
|
|
|
|
|
double fac = Math.log(EcartsReference.get(indexEcartMostUsed)*4/ duration)/Math.log(2);
|
2014-06-11 15:35:06 +02:00
|
|
|
|
buffer.getNote(i).setUnifiedDuration(String.valueOf(Math.round(Math.pow(2, (int) fac))) + (fac % 1 < 0.5 ? "." : ""));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
System.out.println("ECARTS:");
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < UnifiedEcarts.size(); i++) {
|
|
|
|
|
System.out.println("\t" + EcartsReference.get(i) + " (" + UnifiedEcarts.get(i) + ")");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Trie les instants de d<EFBFBD>part des notes dans l'ordre croissant
|
|
|
|
|
* @param buffer le buffer dont les instants sont <EFBFBD> trier */
|
|
|
|
|
private ArrayList<Instant> bufferSortOut(NoteBuffer buffer){
|
|
|
|
|
ArrayList<Instant> tmp = new ArrayList<>();
|
|
|
|
|
ArrayList<Instant> res = new ArrayList<>();
|
|
|
|
|
// On r<>cup<75>re tous les instans de d<>part
|
|
|
|
|
for (int i = 0; i < buffer.getNotesNbr(); i++) {
|
|
|
|
|
tmp.add(buffer.getNote(i).getStart());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Tri par ordre croissant
|
|
|
|
|
res.add(tmp.get(0));
|
|
|
|
|
for (int i = 1; i < tmp.size(); i++) {
|
|
|
|
|
int j = 0;
|
|
|
|
|
while(j<res.size() && res.get(j).isLowerOrEquThan(tmp.get(i))){
|
|
|
|
|
j++;
|
|
|
|
|
}
|
|
|
|
|
res.add(j, tmp.get(i));
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
/** Calcule les <EFBFBD>carts entre les instants de d<EFBFBD>part successifs
|
|
|
|
|
* @param instantList la liste des instants tri<EFBFBD>s par ordre croissant */
|
|
|
|
|
private ArrayList<Float> calculeEcarts(ArrayList<Instant> instantList){
|
|
|
|
|
ArrayList<Float> res = new ArrayList<>();
|
|
|
|
|
for (int i = 1; i < instantList.size(); i++) {
|
|
|
|
|
res.add(instantList.get(i).substract(instantList.get(i - 1)));
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean updateProgress(float progress){
|
|
|
|
|
try {
|
|
|
|
|
Thread.sleep(0);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
this.progress = progress;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float getProgress(){
|
|
|
|
|
if(script == null)
|
|
|
|
|
return -1;
|
|
|
|
|
return script.progress;
|
|
|
|
|
}
|
|
|
|
|
}
|