salut à tous.
Je cherche à réaliser avec Processing une video entièrement générée à partir d'un fichier audio.
Pour le faire en temps réel pas (trop) de problème : je lit le fichier audio, récupère le niveau du canal gauche et droit et réalise un e animation extrêmement dépouillée qui me convient.
Le problème c'est quand je veux sauvegarder cela pour obtenir un fichier AVI mêlant l'audio et la video parfaitement synchrone. je veux une synchro aussi parfaite que possible, je n'utilise donc pas MovieMaker mais plutôt SaveFrame. Je veux une synchro parfaite, si mon fichier audio dure 1 minute, je souhaite 60 sec* 30 image/sec = 1800 images exactement (j'utiliserais ensuite virtualDub pour caler audio et vidéo ). Le mien dure 4min30. A priori la seule solution c'est de faire du Off Line, ne pas générer ni surtout sauvegarder l'animation en temps réel pendant la lecture de l'audio. Mais plutôt faire le traitement à part en mode Off Line.
Mais je n'y arrive pas .
Avec Minim il n'y a pas à ma connaissance de fonction pour accéder à l'audio en OffLine . j'ai essayé de lire l'audio , de sauvegarder les canaux dans des Array, de réaliser l'animation en relisant ensuite les Arrays. J'y arrive pas (taille des buffers, des Arrays, je m'y perd carrément).
Avec Ess c'est possible de lire un audio Off Line, via Spectrum et getSpectrum, mais on accède à la FFT uniquement : cf ce post très instructif http://processing.org/discourse/yabb2/Y … 4169711/45
Alors comment accéder au Level via Spectrum/ESS?
Ou alors avez vous une autre approche????
PS : je ne suis pas programmeur loin s'en faut...
Hors ligne
bon, ben j'y arrive toujours pas...
alors j'ai fait "à la barbare" :
import ddf.minim.*; Minim minim; AudioPlayer song; // int compt =0; // float[] gauchedata = new float[4096]; // float[] droitedata = new float[4096]; void setup() { size(320, 200); minim = new Minim(this); // this loads mysong.wav from the data folder song = minim.loadFile("transition1.wav", 1024*4); song.play(); } void draw() { background(0); stroke(255); // we draw the waveform by connecting neighbor values with a line // we multiply each of the values by 50 // because the values in the buffers are normalized // this means that they have values between -1 and 1. // If we don't scale them up our waveform // will look more or less like a straight line. //println("songsize="+song.right.size()); //println("song buffer size=" + song.bufferSize()); //if ( song.isPlaying() ) // { //println("son en cours"); for(int i = 0; i < song.bufferSize() - 1; i++) { // line(i, 50 + song.left.get(i)*50, i+1, 50 + song.left.get(i+1)*50); // line(i, 150 + song.right.get(i)*50, i+1, 150 + song.right.get(i+1)*50); // println(compt); // gauchedata[i]=song.left.get(i); // droitedata[i]=song.right.get(i); //println(song.bufferSize()); //println(i); //println(gauchedata[i]); point(width/2*(1+song.left.get(i)), height/2*(1+song.right.get(i))); //println("i="+i+" D="+droitedata[i]+" G="+gauchedata[i]); //compt++; //song.pause(); } //song.pause(); // } //else // { //println("fin son"); //for (int k = 1; k<compt-1;k++) // { // for(int i = 0; i < song.bufferSize() - 1; i++) //{ //line(i, 50 + song.left.get(i)*50, i+1, 50 + song.left.get(i+1)*50); //line(i, 150 + song.right.get(i)*50, i+1, 150 + song.right.get(i+1)*50); //point(width/2*(1+gauchedata[i]), height/2*(1+droitedata[i])); //float m=millis(); //if (millis()%33==0) { saveFrame("line-####.jpg"); } saveFrame("line-####.jpg"); } //saveFrame("line-####.jpg"); //song.play(); //} //} //} void stop() { song.close(); minim.stop(); super.stop(); }
j'ai modifié le fichier wav dans Audacity pour supprimer le blanc en début et fin, et avoir sa durée exacte.
Puis lancé ce script simpliste Processing. supprimé les images inutiles, compté les images restante, calculé le frame rate en fonction de la durée du wav (dans les 22-23 images/sec) .
Puis j'ai chargé les images et le wav dans virtualDub pour caler l'ensemble (modifier le framerate video , décaler le départ du wav).
ça donne ça :
http://vids.myspace.com/index.cfm?fusea … d=55966329
Donc je suis preneur d'astuces sous processing pour éviter tout ça...
A votre bon coeur...
Hors ligne
J'ai le même problème mais avec du midi en entré à la place du wav, j'imagine que c'est plus facile de charger le midi et de le lire offline.
C'est un peu problèmatique pour un soft de génération de vidéo de pas savoir comment exporter son boulot proprement...
Hors ligne
Salut Minamata et bienvenue
J'ai eu le même problème pour une animation synchronisée sur du son, j'ai fait comme toi, pour la réalisation j'envoyais la musique et les images en même temps, par contre pour l'enregistrement, pas de son et images sauvées avec saveframe avec un framerate faible, l'animation finale est à 25 i/s et j'ai fait l'assemblage des images et du son avec ffmpeg. La différence, c'est que les images étaient calées sur le son mais pas *calculées* en fonction du son...
Peut-être que dans ton cas, le mieux est de faire 2 étapes, une première d'analyse du son qui enregistre les informations extraites du son dans des fichiers textes, une seconde ou les images sont fabriquées en lisant ces fichiers texte dans des tableaux et en calculant image par image. Pour calculer les images, il faut que tu aies 25 "données sonores" par seconde, ce qui correspond à 1764 frames sonores (à 44100 Hz), dans la méthode getSpectrum d'ESS il y a un paramètre offset pour démarrer l'analyse fft à partir d'une frame sonore donnée.
Les résultats bruts d'une fft traditionnelle ne sont pas toujours pertinents, un découpage par octave peut parfois être plus intéressant :
http://www.davebollinger.com/works/p5/fftoctana/
code : http://www.davebollinger.com/works/p5/f … er.pde.txt
Hors ligne
Salut emoc
merci de ta réponse, j'insiste.
Effectivement, ESS avec spectrum et getSpectrum marche à la perfection
ci après le code de Dave Bollinder je pense :
import krister.Ess.*; AudioChannel myChannel; FFT fft; int frameNumber = 0; int framesPerSecond = 30; void setup() { size(320,240,P3D); Ess.start(this); myChannel = new AudioChannel(dataPath("noir1.mp3")); fft = new FFT(512); fft.limits(); } public void stop() { Ess.stop(); super.stop(); } void draw() { analyze(); render(); advance(); } void analyze() { fft.getSpectrum(myChannel.samples, (int)(frameNumber * myChannel.sampleRate / framesPerSecond)); } void render() { background(255); fill(0); noStroke(); for (int i=0; i<256; i++) { float sp = fft.spectrum[i]; rect(32+i,230,1,-sp*220); } // pretend the rendering took a long time just as proof of concept... try { Thread.sleep(100); } catch(InterruptedException e) {} // save it saveFrame("out\\img_"+nf(frameNumber,6)+".jpg"); } void advance() { frameNumber++; }
Mais ça ne marche, à ma connaissance, que pour la FFT; mon animation n'utilise que le Niveau Droite et Gauche, et du coup je n'arrive pas à adapter le code....c'est ballot
un coup de main?
Hors ligne
Je viens de me rendre compte qu'Ess ne travaillait qu'en mono... peut-être que c'est contournable en créant 2 AudioChannel dans lesquels les canaux G et D sont chargés, (pour l'analyse dans une première phase)
Sinon, dans la classe FFT d'Ess il existe la méthode getLevel, qui récupère le niveau sonore, ça correspond à ce que tu cherches.
En remplaçant getSpectrum par getLevel, c'est dans la poche
Hors ligne
alors la emoc je suis largué - j'avoue.
je croyais que spectrum et getSpectrum allaient d epair : getSpectrum cacule la FFT ets la stocke dans un buffer interrogeable par spectrum même en offline
alors que getLevel renvoie le volume mais cette fois en temps réel uniquement....
me trompé-je ??
bon si je suis un boulet, faut le dire aussi hein.... promis
Hors ligne
salut,
bon, faute de comprendre les subtilités du offline dans ESS avec getLevel, je suis revenu à Minim en essayant de faire 1/ d'l'analyse du son et de stocker les valeurs dans un fichier texte puis 2/ de lire le fichier texte, réaliser l'animation et sauvegrader la video (saveframe) ou directement la video (mmoviemaker)
le premier script:
import ddf.minim.*; import ddf.minim.analysis.*; import processing.opengl.*; Minim minim; AudioPlayer song; BeatDetect beat; int r=2; int t0=0; int[] x = new int[0]; void setup() { //size(1024, 768, OPENGL); frameRate(60); size(1024/r, 768/r, OPENGL); background(150); noStroke(); // Instantiate cubes, passing in random vals for size and postion minim = new Minim(this); // this loads mysong.wav from the data folder song = minim.loadFile("noir2.wav", 1024*4); println(song.length()); song.play(); t0=millis(); println(t0); // t0=millis(); // t1=t0+song.length(); //println(t0+" "+t1); beat = new BeatDetect(); beat.setSensitivity(400); //while (song.isPlaying()) { //beat.detect(song.mix); //if ( beat.isOnset() ) { // x=append(x, millis()); // println(millis()); //} //} } void draw() { beat.detect(song.mix); if (song.isPlaying()) { if ( beat.isOnset() ) { x=append(x, millis()-t0); // println(millis()-t0); // println(millis()); } } } void keyPressed() { // Press a key to save the data String[] lines = new String[x.length]; for (int i = 0; i < x.length; i++) { lines[i] = str(x[i]); // println(x[i]); } saveStrings("lines.txt", lines); exit(); // Stop the program }
le 2ème:
citation :
import ddf.minim.*;
import ddf.minim.analysis.*;
Minim minim;
AudioPlayer song;
//BeatDetect beat;
String[] lines;
int index = 0;
int t0=0;
int top=0;
Particle[] particles;
int numParticles = 4095;
boolean lignes = false;
int w = 320;
int h = 200;
float cX=w/2;
float cY=h/2;
void setup()
{
// frameRate(15);
size(w, h);
background(0);
particles = new Particle[numParticles];
for(int i=0;i<numParticles;i++)
{
particles[i] = new Particle();
particles[i].x = random(w);
particles[i].y = random(h);
particles[i].vx = random(-1, 1);
particles[i].vy = random(-1, 1);
}
lines = loadStrings("lines.txt");
t0=millis();
println("lines.length="+lines.length);
minim = new Minim(this);
// this loads mysong.wav from the data folder
// song = minim.loadFile("noir2.wav", 1024*4);
// song.play();
//beat = new BeatDetect();
// beat.setSensitivity(200);
}
void draw()
{
// beat.detect(song.mix);
fill(0, 10);
rect(0, 0, w, h);
// println(t0);
// println(millis());
for(int i=0;i<numParticles -1 ;i++)
{
if ( index < lines.length ) {
if (int(lines[index]) <= (millis()-t0)) {
cX= cX +random(w/4);
cY=cY+ random(h/4);
cX=cX%w;
cY=cY%h;
println(index+" "+lines[index]);
index++;
}
}
particles[i].move(cX, cY);
particles[i].render();
}
//saveFrame("line-####.jpg");
if ((index==lines.length)&&(top==0)) {
top = millis();
println("TOP="+top);
}
if ((top>0) && (millis()>=top+13547)) {
println("EXIT"+millis());
exit();
}
}
void mousePressed()
{
//
}
void stop()
{
//song.close();
//minim.stop();
// super.stop();
}
ben ça marche pas bien: le rendu enregistré est très en dessous de la qualité du temps réél.
Dans cet exemple, ce n'est donc pas tant l'analyse du son en temps réél qui perturbe l'enregistrement, mais bien l'enregistrement lui même des images..
http://vids.myspace.com/index.cfm?fusea … d=56558108
il faudrait animer image par image et sauvegarder image par images également.... j'imagine.
que de boulot pour garder une trace d'un script qui marche très bien en temps réel....
Hors ligne
Salut,
J'ai fait un essai avec getLevel() en reprenant le code ci-dessus (http://codelab.fr/1106#p5142), ça a l'air de fonctionner en analyse hors temps réel, ou alors il y a quelque chose que je ne pige pas... Il faut 3 paramètres à getLevel (float[] int int) ce qui n'est pas indiqué dans la doc, je ne sais pas à quoi sert le dernier!
Autre changement, j'enregistre les images en png à 25 i/s, png c'est non-compressé par rapport au jpeg, l'assemblage son + images est fait avec ffmpeg dont voila la commande :
ffmpeg -i img_%06d.png -i bloup.wav -map 0:0 -map 1:0 -r 25 -ac 2 -b 360k -ab 192k -s 320x240 -f flv video.flv
import krister.Ess.*; AudioChannel myChannel; FFT fft; int frameNumber = 0; int framesPerSecond = 25; float bg; void setup() { size(320,240,P3D); Ess.start(this); myChannel = new AudioChannel(dataPath("bloup.wav")); fft = new FFT(512); fft.limits(); } public void stop() { Ess.stop(); super.stop(); } void draw() { analyze(); render(); advance(); } void analyze() { fft.getSpectrum(myChannel.samples, (int)(frameNumber * myChannel.sampleRate / framesPerSecond)); int m = 100; float l = fft.getLevel(myChannel.samples, (int)(frameNumber * myChannel.sampleRate / framesPerSecond), m); println(l*100); bg = 255 - (l*255); } void render() { background(bg); fill(0); noStroke(); for (int i=0; i<256; i++) { float sp = fft.spectrum[i]; rect(32+i,230,1,-sp*220); } // pretend the rendering took a long time just as proof of concept... try { Thread.sleep(100); } catch(InterruptedException e) {} // save it saveFrame("out\\img_"+nf(frameNumber,6)+".png"); } void advance() { frameNumber++; }
Et ça donne :
Hors ligne
Pages: 1