Desenvolupament d'aplicacions d'àudio amb Minim

Índex
- Introducció
- 1.Breu introducció a la programació orientada a objectes
- 2.La biblioteca d'àudio per Processing Minim
- 2.1.Què és Minim?
- 2.2.Treballant amb Minim
- 2.3.Les UGens de Minim
- 2.3.1.Generadors de so
- 2.3.2.Efectes
- 2.3.3.Envolupants
- 2.3.4.Matemàtiques
- 2.3.5.Utilitats
- 2.4.La POO i el desenvolupament d'aplicacions d'àudio amb Minim
- 2.5.Accés a la documentació dels mètodes i atributs de les UGens
- 3.Estudi d'exemples
- 3.1.Reproductor d'àudio
- 3.2.Efectes
- 3.3.Anàlisi FFT
- 3.4.Síntesis additiva
- Bibliografia
Introducció
1.Breu introducció a la programació orientada a objectes
1.1.Conceptes fonamentals de la programació orientada a objectes
Objecte: telèfon mòbil de Roger F.
|
---|
Característiques |
Marca: Samsung Model: Galaxy S4 I9505 Color: Negre Estat de la bateria: Esgotada |
Comportaments |
Trucar Fer fotografies Encendre Apagar |

1.2.Treballant amb classes i objectes en Processing
// Nom de la classe class MyRectangle { // Atributs float xpos; float ypos; float xlength; float ylength; // Mètodes de la classe // Constructor MyRectangle(float tempXpos, float tempYpos, float tempXlength, float tempYlength) { xpos = tempXpos; ypos = tempYpos; xlength = tempXlength; ylength = tempYlength; } void display() { rect(xpos, ypos, xlength, ylength); } }
class MyRectangle {
// Constructor 1: construeix rectangles de mides variables MyRectangle(float tempXpos, float tempYpos, float tempXlength, float tempYlength) { xpos = tempXpos; ypos = tempYpos; xlength = tempXlength; ylength = tempYlength; } // Constructor 2: construeix rectangles de mides constants MyRectangle(float tempXpos, float tempYpos) { xpos = tempXpos; ypos = tempYpos; xlength = 10; ylength = 10; }
// Declarem un objecte de classe MyRectangle MyRectangle myRect; void setup() { size(400, 400); // Inicialitzem l'objecte cridant al seu constructor myRect = new MyRectangle(10, 10, 30, 50); // Cridem al mètode display() de l'objecte myRect per dibuixar el rectangle // a la finestra de l'aplicació myRect.display(); } void draw() { } // Aquí va inserit el codi de la classe MyRectangle
MyRectangle myRect;
myRect = new MyRectangle(10, 10, 30, 50);
xpos = 10; ypos = 10; xlength = 30; ylength = 50;
myRect.display();
rect(10, 10, 30, 50);

// Declarem dos objectes de la classe MyRectangle MyRectangle myRect00; MyRectangle myRect01; void setup() { size(400, 400); // Inicialitzem el dos objectes amb els seus constructors myRect00 = new MyRectangle(10, 10, 30, 50); myRect01 = new MyRectangle(60, 30, 80, 30); // Cridem al mètode display() dels objectes per dibuixar els rectangles // a la finestra de l'aplicació myRect00.display(); myRect01.display(); } void draw() { } // Aquí va inserit el codi de la classe MyRectangle
// Inicialitzem el dos objectes amb el constructor myRect00 = new MyRectangle(10, 10, 30, 50); myRect01 = new MyRectangle(60, 30, 80, 30);



// Nom de la classe class MyEllipse { // Atributs float xpos; float ypos; float diameter; // Mètodes de la classe // Constructor MyEllipse(float tempXpos, float tempYpos, float tempDiameter) { xpos = tempXpos; ypos = tempYpos; diameter = tempDiameter; } void display() { ellipse(xpos, ypos, diameter, diameter); } void echo() { for (int i=0; i<5; i++) { float newDiameter = diameter/(i+2); ellipse(xpos, ypos, newDiameter, newDiameter); } } }
// Declarem els diferents objectes MyRectangle myRect00; MyRectangle myRect01; MyEllipse myEllip; void setup() { size(400, 400); // Inicialitzem els dos objectes de la classe MyRectangle amb els constructors myRect00 = new MyRectangle(10, 10, 30, 50); myRect01 = new MyRectangle(60, 30, 80, 30); // Inicialitzem també l'objecte myEllip myEllip = new MyEllipse(200, 200, 180); // Cridem al mètode display() dels objectes per dibuixar-los a la // finestra de l'aplicació myRect00.display(); myRect01.display(); myEllip.display(); // Cridem al mètode echo() de l'objecte myEllip // Aquest mètode dibuixa el·lipses dins de l'el·lipse principal myEllip.echo(); } void draw() { } // Aquí va inserit el codi de les classes MyRectangle i MyEllipse

myRect00.echo()
2.La biblioteca d'àudio per Processing Minim
2.1.Què és Minim?
2.2.Treballant amb Minim
/** * Minim. Exemple 1 * Francesc Martí, martifrancesc@uoc.edu, 15-10-2014 * * Aquest sketch simplement genera una ona de tipus sinusoïdal, a 440 Hz * i 0.5 d'amplitud * */ // Importem els paquets necessaris de la biblioteca Minim import ddf.minim.*; import ddf.minim.ugens.*; // Definim els objectes Minim minim; Oscil myWave; AudioOutput out; void setup() { size(512, 200); // Inicialitzem l'objecte de la classe Minim minim = new Minim(this); // Inicialitzem un oscil·lador amb forma d'ona de tipus sinusoïdal // amb freqüència 440 Hz i amplitud 0.5 myWave = new Oscil( 440, 0.5f, Waves.SINE ); // Fem servir el mètode getLineOut de l'objecte minim per inicialitzar un // objecte AudioOutput out = minim.getLineOut(); // Finalment, connectem l'oscil·lador a la sortida d'àudio myWave.patch(out); } void draw() { }
import ddf.minim.*; import ddf.minim.ugens.*;
import ddf.minim.spi.*; import ddf.minim.signals.*; import ddf.minim.*; import ddf.minim.analysis.*; import ddf.minim.ugens.*; import ddf.minim.effects.*;
// Declaració Minim minim; // ... // Inicialització de l'objecte minim amb el seu constructor minim = new Minim(this);
// Declaració Oscil myWave; // ... // Inicialització de l'objecte myWave amb el seu constructor myWave = new Oscil(440, 0.5f, Waves.SINE);
-
freqüència de l'oscil·lador (440 Hz),
-
amplitud de l'oscil·lador (0.5) i
-
forma d'ona de l'oscil·lador (ona de tipus sinusoïdal).
// Declaració AudioOutput out; // ... // Inicialització out = minim.getLineOut();
out = minim.getLineOut(Minim.MONO, 512, 48000, 24);
myWave.patch(out);
2.3.Les UGens de Minim

2.3.1.Generadors de so
-
Noise: és una UGen que genera soroll de tipus blanc, rosa o marró.
-
Oscil: genera ones periòdiques a una determinada freqüència i amplitud. Poden fer-se servir com a LFO.
-
LiveInput: captura l'entrada d'àudio de la interfície d'àudio de l'ordinador.
-
FilePlayer: reprodueix l'àudio des d'un fitxer.
-
Sampler: és la UGen optimitzada per reproduir sons de curta durada.
-
GranulateSteady: UGen que permet treballar amb síntesi granular.
-
GranulateRandom: similar a la UGen GranulateSteday, produeix grans de so de longituds aleatòries.
2.3.2.Efectes
-
Delay: genera repeticions amb retard del senyal d'entrada.
-
Pan: situa en una posició estereofònica el senyal monofònic d'entrada.
-
Balance: atenua el canal dret o esquerra d'un senyal estereofònic.
-
Gain: atenua o amplifica el senyal d'entrada.
-
MoogFilter: versió digital del filtre analògic 24 dB/octava de Moog. Pot configurar-se com a filtre passa baixos, passa alts o passa banda.
-
BitCrush: produeix distorsió al reduir la resolució del senyal d'entrada.
-
WaveShaper: utilitza el senyal d'entrada com a índex d'una taula d'ones.
-
Flanger: efecte basat en retards en la suma de dos senyals idèntics, aplicant-li a una d'elles un retard variable.
-
Vocoder: efecte basat en l'anàlisi i posterior síntesi de l'entrada d'àudio. És l'efecte que genera les típiques veus robotitzades.
2.3.3.Envolupants
-
Line: genera una envolupant lineal entre els dos punts donats.
-
ADSR: genera una típica envolupant amb atac, decaïment, sosteniment i relaxació.
-
Damp: genera una envolupant amb atac i decaïment.
2.3.4.Matemàtiques
-
Abs: genera el valor absolut dels valors d'entrada.
-
Constant: serveix per definir un valor constant.
-
Midi2Hz: genera la freqüència equivalent en Hz d'un valor que representa una nota MIDI.
-
Multiplier: multiplica el senyal d'entrada per un valor d'amplitud.
-
Reciprocal: genera el valor recíproc del valor d'entrada.
-
Summer: suma tots els senyals d'entrada.
2.3.5.Utilitats
-
Bypass: aquesta UGen permet configurar altres UGen en mode bypass.
-
EnvelopeFollower: analitza l'entrada d'àudio i genera un valor que representa la seva amplitud.
-
TickRate: permet canviar la velocitat del rellotge intern d'una UGen.
-
Sink: és una UGen similar al Summer, però, en comptes de sumar totes les entrades d'àudio, simplement genera silenci. Es fa servir per tenir operatives UGens que no generen so (com una EnvelopeFollower).
2.4.La POO i el desenvolupament d'aplicacions d'àudio amb Minim

2) Adquirir un oscil·lador.
3) Adquirir una sortida d'àudio.
4) Configurar la carcassa de l'instrument.
5) Configurar l'oscil·lador.
6) Configurar la sortida d'àudio.
7) Connectar l'oscil·lador a la sortida d'àudio.
Món real
|
Processing amb Minim
|
---|---|
Adquirir la carcassa del instrument |
Minim minim; |
Adquirir un oscil·lador |
Oscil myWave; |
Adquirir una sortida d'àudio |
AudioOutput out; |
Configurar la carcassa del instrument |
minim = new Minim(this); |
Configurar l'oscil·lador |
myWave = new Oscil(440, 0.5f, Waves.SINE); |
Configurar la sortida d'àudio |
out = minim.getLineOut(); |
Connectar l'oscil·lador a la sortida d'àudio |
myWave.patch(out); |

2.5.Accés a la documentació dels mètodes i atributs de les UGens


-
setFrequency (float hz): estableix la freqüència de l'oscil·lador. La nova freqüència vindrà determinada pel paràmetre hz.
-
setAmplitude (float newAmp): estableix l'amplitud de l'oscil·lador. La nova amplitud vindrà determinada pel valor newAmp.
-
setWaveform (Waveform theWaveForm): canvia la forma d'ona utilitzada per l'oscil·lador. En aquest cas, el paràmetre d'aquest métode són unes formes d'ona ja predefinides a Minim: Waves.PHASOR, Waves.QUARTERPULSE, Waves.SAW, Waves.SINE, Waves.SQUARE i Waves.TRIANGLE.


float amp = map(mouseY, 0, height, 1, 0); myWave.setAmplitude(amp);
float freq = map(mouseX, 0, width, 150, 1000); myWave.setFrequency(freq);
myWave.setWaveform(Waves.SQUARE);
// Dibuixem la forma d'ona que es genera a la finestra de l'aplicació for (int i = 0; i < out.bufferSize() - 1; i++) { line(i, 50 - out.left.get(i)*50, i+1, 50 - out.left.get(i+1)*50); line(i, 150 - out.right.get(i)*50, i+1, 150 - out.right.get(i+1)*50); }
/** * Minim. Exemple 2 * Francesc Martí, martifrancesc@uoc.edu, 15-10-2014 * * Aquest sketch permet controlar la freqüència, amplitud i forma d'ona d'un * oscil·lador mitjançant el ratolí i el teclat de l'ordinador. * La forma d'ona que es genera es dibuixa a la finestra de l'aplicació. * * Funcionament * mouseX: modifica la freqüència de l'oscil·lador entre 150 Hz i 1000 Hz. * mouseY: modifica l'amplitud de l'oscil·lador. * * tecla '1': forma d'ona de l'oscil·lador Sinusoïdal * tecla '2': forma d'ona de l'oscil·lador Triangular * tecla '3': forma d'ona de l'oscil·lador Dent de serra * tecla '4': forma d'ona de l'oscil·lador Quadrada * */ // Importem els paquets necessaris de la biblioteca Minim import ddf.minim.*; import ddf.minim.ugens.*; // Definim els objectes Minim minim; Oscil myWave; AudioOutput out; void setup() { size(512, 200); // Inicialitzem l'objecte de la classe Minim minim = new Minim(this); // Inicialitzem un oscil·lador amb forma d'ona de tipus sinusoïdal // amb freqüència 440 Hz i amplitud 0.5 myWave = new Oscil(440, 0.5f, Waves.SINE); // Fem servir el mètode getLineOut de l'objecte minim per inicialitzar un // objecte AudioOutput out = minim.getLineOut(); // Finalment, connectem l'oscil·lador a la sortida d'àudio myWave.patch(out); } void draw() { background(0); stroke(255, 255, 0); strokeWeight(1); // Dibuixem la forma d'ona que es genera a la finestra de l'aplicació for (int i = 0; i < out.bufferSize() - 1; i++) { line(i, 50 - out.left.get(i)*50, i+1, 50 - out.left.get(i+1)*50); line(i, 150 - out.right.get(i)*50, i+1, 150 - out.right.get(i+1)*50); } } void mouseMoved() { // Cada cop que el ratolí es mogui s'executa aquesta funció // Els mètodes de l'objecte myWave setAmplitude() i setFrequency() modifiquen // les propietats Amplitud i Freqüència de l'oscil·lador float amp = map(mouseY, 0, height, 1, 0); myWave.setAmplitude(amp); float freq = map(mouseX, 0, width, 150, 1000); myWave.setFrequency(freq); } void keyPressed() { // Cada cop que es premi una tecla s'executa aquesta funció // Si es prem una tecla entre 1 i 4 s'executa aquest codi i es canvia la // forma d'ona de l'oscil·lador switch(key) { case '1': myWave.setWaveform(Waves.SINE); break; case '2': myWave.setWaveform(Waves.TRIANGLE); break; case '3': myWave.setWaveform(Waves.SAW); break; case '4': myWave.setWaveform(Waves.SQUARE); break; default: break; } }
3.Estudi d'exemples
3.1.Reproductor d'àudio

AudioRecordingStream myFile = minim.loadFileStream(fileName, 1024, true);
fileName = "song.mp3";
/** * Minim. Exemple 3 * Francesc Martí, martifrancesc@uoc.edu, 15-10-2014 * * Aquest sketch simplement reprodueix un arxiu d'àudio digital. * La forma d'ona que es genera es dibuixa a la finestra de l'aplicació. * * Funcionament * tecla 's': atura la reproducció o la reprèn en mode loop si aquesta * estava aturada. * */ // Importem els paquets necessaris de la biblioteca Minim // Atenció! Per fer servir la classe AudioRecordingStream hem d'importar // també el paquet ddf.minim.spi import ddf.minim.*; import ddf.minim.ugens.*; import ddf.minim.spi.*; // Definim els objectes Minim minim; FilePlayer myFilePlayer; AudioRecordingStream myFile; AudioOutput out; // Aquesta variable guarda el nom de l'arxiu d'audio String fileName; void setup() { size(600, 200); // Inicialitzem l'objecte de la classe Minim minim = new Minim(this); // La variable fileName conté la ruta i el nom de l'arxiu d'àudio fileName = "song.mp3"; // Inicialitzem l'objecte myFile // fileName és el nom de l'arxiu d'àudio, 1024 la mida del buffer // i 'true' indica que el so es carrega a la memòria myFile = minim.loadFileStream(fileName, 1024, true); // Inicialitzem el reproductor d'àudio myFilePlayer amb la "cinta" myFile myFilePlayer = new FilePlayer(myFile); // Fem que l'arxiu d'àudio es reprodueixi en mode loop myFilePlayer.loop(); // Fem servir el mètode getLineOut de l'objecte minim per inicialitzar un // objecte AudioOutput out = minim.getLineOut(); // Finalment, connectem el reproductor a la sortida d'àudio myFilePlayer.patch(out); } void draw() { background(0); stroke(255, 255, 0); strokeWeight(1); // Dibuixem la forma d'ona a la finestra de l'aplicació for (int i = 0; i < out.bufferSize() - 1; i++) { line( i, 50 - out.left.get(i)*50, i+1, 50 - out.left.get(i+1)*50 ); line( i, 150 - out.right.get(i)*50, i+1, 150 - out.right.get(i+1)*50 ); } } void keyPressed() { switch( key ) { case 's': // Depèn de si el so s'està reproduint o no, // l'aturem o iniciem la seva reproducció if ( myFilePlayer.isPlaying() ) { // Aturem la reproducció myFilePlayer.pause(); } else { // Comencem a reproduir de nou tot l'arxiu en mode loop myFilePlayer.loop(); } break; default: break; } }
3.2.Efectes
-
la freqüència de tall (800 Hz),
-
la ressonància del filtre (0,5) i
-
especificar si volem que sigui un filtre passa baixos, passa alts o passa banda.
moog = new MoogFilter(800, 0.5, MoogFilter.Type.LP);
void mouseMoved(){ // ... // La freqüència de tall pot anar de 200 Hz a 12 KHz float freq = map(mouseX, 0, width, 200, 12000); float res = map(mouseY, height, 0, 0, 1); moog.frequency.setLastValue(freq); moog.resonance.setLastValue(res);
void keyPressed() { switch(key) { // ... case 'q': moog.type = MoogFilter.Type.LP; break; case 'w': moog.type = MoogFilter.Type.HP; break; case 'e': moog.type = MoogFilter.Type.BP; break;
moog.type = MoogFilter.Type.LP;
text("Nom de l'arxiu d'àudio", 10, 225); text(fileName, 10, 245); text("Tipus de filtre: " + moog.type, 430, 225); text("Freqüència de tall: " + moog.frequency.getLastValue() + " Hz", 430, 245); text("Resonàcia del filtre: " + moog.resonance.getLastValue(), 430, 265);
/** * Minim. Exemple 4 * Francesc Martí, martifrancesc@uoc.edu, 15-10-2014 * * Aquest sketch reprodueix un arxiu d'àudio digital i li aplica un * filtre analògic 24 dB/octava de Moog. * La forma d'ona que es genera es dibuixa a la finestra de l'aplicació. * * Funcionament * mouseX: modifica la freqüència de tall del filtre (200 Hz a 12 Khz). * mouseY: modifica la ressonància del filtre. * * tecla 'q': filtre de tipus passa baixos * tecla 'w': filtre de tipus passa alts * tecla 'e': filtre de tipus passa banda * * tecla 's': forma d'ona de l'oscil·lador Quadrada * */ // Importem els paquets necessaris de la biblioteca Minim import ddf.minim.*; import ddf.minim.ugens.*; import ddf.minim.spi.*; // Definim els objectes Minim minim; FilePlayer myFilePlayer; AudioRecordingStream myFile; MoogFilter moog; AudioOutput out; // Aquesta variable guarda el nom de l'arxiu d'àudio String fileName; void setup() { size(640, 280); // Inicialitzem l'objecte de la classe Minim minim = new Minim(this); // La variable fileName conté la ruta i el nom de l'arxiu d'àudio fileName = "song.mp3"; // Inicialitzem l'objecte myFile // fileName és el nom de l'arxiu d'àudio, 1024 la mida del buffer // i 'true' indica que el so es carrega a la memòria myFile = minim.loadFileStream(fileName, 1024, true); // Inicialitzem el reproductor d'àudio myFilePlayer amb la "cinta" myFile myFilePlayer = new FilePlayer(myFile); // Fem que l'arxiu d'àudio es reprodueixi en mode loop myFilePlayer.loop(); // Construim un filtre de tipus Low Pass, freqüència de tall 800 Hz // i ressonància 0.5 moog = new MoogFilter(800, 0.5, MoogFilter.Type.LP); // Fem servir el mètode getLineOut de l'objecte minim per inicialitzar un // objecte AudioOutput out = minim.getLineOut(); // Connectem el reproductor al filtre myFilePlayer.patch(moog); // Finalment, connectem el filtre al nostre out moog.patch(out); } void draw() { background(0); stroke(255, 255, 0); strokeWeight(1); // Dibuixem la forma d'ona a la finestra de l'aplicació for (int i = 0; i < out.bufferSize() - 1; i++) { line( i, 50 - out.left.get(i)*50, i+1, 50 - out.left.get(i+1)*50 ); line( i, 150 - out.right.get(i)*50, i+1, 150 - out.right.get(i+1)*50 ); } // A la finestra de l'aplicació, mostrem informació sobre // els valors actuals del filtre text("Nom de l'arxiu d'àudio", 10, 225); text(fileName, 10, 245); text("Tipus de filtre: " + moog.type, 430, 225); text("Freqüència de tall: " + moog.frequency.getLastValue() + " Hz", 430, 245); text("Ressonància del filtre: " + moog.resonance.getLastValue(), 430, 265); } // Des del teclat controlem el reproductor (aturar i play) // Ara també controlem el tipus de filtre void keyPressed() { switch(key) { case 's': // Depèn de si el so s'està reproduint o no, // l'aturem o iniciem la seva reproducció if (myFilePlayer.isPlaying()) { // Aturem la reproducció myFilePlayer.pause(); } else { // Comencem a reproduir de nou tot l'arxiu en mode loop myFilePlayer.loop(); } break; case 'q': moog.type = MoogFilter.Type.LP; break; case 'w': moog.type = MoogFilter.Type.HP; break; case 'e': moog.type = MoogFilter.Type.BP; break; default: break; } } // Controlem la freqüència i la ressonància del filtre amb el moviment del ratolí void mouseMoved() { // La freqüència de tall pot anar de 200 Hz a 12 KHz float freq = map(mouseX, 0, width, 200, 12000); float res = map(mouseY, height, 0, 0, 1); moog.frequency.setLastValue(freq); moog.resonance.setLastValue(res); }
3.3.Anàlisi FFT
-
la mida del buffer de l'objecte out i
-
la velocitat de mostreig a la que estem treballant.
fft = new FFT(1024, 44100);
fft.forward(out.mix);
/** * Minim. Exemple 5 * Francesc Martí, martifrancesc@uoc.edu, 15-10-2014 * * Aquest sketch reprodueix un arxiu d'àudio digital i li aplica un * filtre analògic 24 dB/octava de Moog. * La forma d'ona que es genera es dibuixa a la finestra de l'aplicació. * L'anàlisi espectral del so generat també es mostra a la finestra de * l'aplicació. * * Funcionament * mouseX: modifica la freqüència de tall del filtre (200 Hz a 12 Khz). * mouseY: modifica la ressonància del filtre. * * tecla 'q': filtre de tipus passa baixos * tecla 'w': filtre de tipus passa alts * tecla 'e': filtre de tipus passa banda * * tecla 's': forma d'ona de l'oscil·lador Quadrada * */ // Importem els paquets necessaris de la biblioteca Minim // Atenció! Per fer servir l'anàlisi espectral hem d'incloure // el paquet ddf.minim.analysis import ddf.minim.*; import ddf.minim.ugens.*; import ddf.minim.spi.*; import ddf.minim.analysis.*; // Definim els objectes Minim minim; FilePlayer myFilePlayer; AudioRecordingStream myFile; MoogFilter moog; FFT fft; // Definim l'objecte necessari per realitzar l'anàlisi FFT AudioOutput out; // Aquesta variable guarda el nom de l'arxiu d'àudio String fileName; void setup() { size(640, 400); // Inicialitzem l'objecte de la classe Minim minim = new Minim(this); // La variable fileName contindrà el nom de l'arxiu d'àudio fileName = "song.mp3"; // Inicialitzem l'objecte myFile // fileName és el nom de l'arxiu d'àudio, 1024 la mida del buffer // i 'true' indica que el so es carrega a la memòria myFile = minim.loadFileStream(fileName, 1024, true); // Inicialitzem el reproductor d'àudio myFilePlayer amb la "cinta" myFile myFilePlayer = new FilePlayer(myFile); // Fem que l'arxiu d'àudio es reprodueixi en mode loop myFilePlayer.loop(); // Construim un filtre de tipus Low Pass, freqüència de tall 800 Hz // i ressonància 0.5 moog = new MoogFilter(800, 0.5, MoogFilter.Type.LP); // Construim l'objecte fft fft = new FFT(1024, 44100); // Fem servir el mètode getLineOut de l'objecte minim per inicialitzar un // objecte AudioOutput out = minim.getLineOut(); // Connectem el reproductor al filtre myFilePlayer.patch(moog); // Finalment, connectem el filtre al nostre out moog.patch(out); } void draw() { background(0); stroke(255, 255, 0); strokeWeight(1); // Dibuixem la forma d'ona a la finestra de l'aplicació for (int i = 0; i < out.bufferSize() - 1; i++) { line(i, 50 - out.left.get(i)*50, i+1, 50 - out.left.get(i+1)*50); line(i, 150 - out.right.get(i)*50, i+1, 150 - out.right.get(i+1)*50); } // A la finestra de l'aplicació, mostrem informació sobre // els valors actuals del filtre text("Nom de l'arxiu d'àudio", 10, 225); text(fileName, 10, 245); text("Tipus de filtre: " + moog.type, 430, 225); text("Freqüència de tall: " + moog.frequency.getLastValue() + " Hz", 430, 245); text("Ressonància del filtre: " + moog.resonance.getLastValue(), 430, 265); // Fem l'anàlisi fft sobre el buffer mix fft.forward(out.mix); // Dibuixem l'espectre a la finestra de l'aplicació // Multipliquem el valor de l'anàlisi (getBand) per 8 per visualitzar // millor l'espectre for (int i = 0; i < out.bufferSize() - 1; i++) { // Espectre en vermell stroke(255, 0, 0); line(i, height, i, height - fft.getBand(i)*8); } } // Des del teclat controlem el reproductor (aturar i play) // Ara també controlem el tipus de filtre void keyPressed() { switch(key) { case 's': // Depèn de si el so s'està reproduint o no, // l'aturem o iniciem la seva reproducció if ( myFilePlayer.isPlaying() ) { // Aturem la reproducció myFilePlayer.pause(); } else { // Comencem a reproduir de nou tot l'arxiu en mode loop myFilePlayer.loop(); } break; case 'q': moog.type = MoogFilter.Type.LP; break; case 'w': moog.type = MoogFilter.Type.HP; break; case 'e': moog.type = MoogFilter.Type.BP; break; default: break; } } // Controlem la freqüència i la ressonància del filtre amb el moviment // del ratolí void mouseMoved() { // La freqüència de tall pot anar de 200 Hz a 12 KHz float freq = map(mouseX, 0, width, 200, 12000); float res = map(mouseY, height, 0, 0, 1); moog.frequency.setLastValue(freq); moog.resonance.setLastValue(res); }

3.4.Síntesis additiva

sum = new Summer();
for (int i = 0; i < numHarm ; i++) { myWave[i] = new Oscil(freqFonamental*(i+1), 0.1f, Waves.SINE ); myWave[i].patch(sum); }
freqFonamental*(i+1)

/** * Minim. Exemple 6 * Francesc Martí, martifrancesc@uoc.edu, 15-10-2014 * * Aquest sketch és un senzill sintetitzador de síntesis additiva. * El so generat consisteix en una ona sinusoïdal fonamental sumada als seus * primers 9 harmònics. * * Hi ha 4 formes de relacionar l'amplitud dels harmònics respecte a l'amplitud * de la fonamental. * Mètode Suma 1: f(0)=1, f(1)=1/2, f(2)=1/3, etc. La suma dels harmònics * tendeix a una ona amb forma Dent de serra. * Mètode Suma 2: f(0)=1, f(1)=0, f(2)=1/3, f(3)=0, etc. La suma dels harmònics * tendeix a una ona amb forma Quadrada. * Mètode Suma 3: f(0)=1, f(1)=0, f(2)=1/9, f(3)=0, f(4)=1/25. etc. La suma dels * harmònics tendeix a una ona amb forma Triangular. * Mètode Suma 4 (forma per defecte): f(0)=1, f(1)=1, f(2)=1, etc. * * La forma d'ona que es genera es dibuixa a la finestra de l'aplicació. * L'anàlisi espectral del so generat també es mostra a la finestra de * l'aplicació. * * Funcionament * mouseX: modifica la freqüència de la fonamental (entre 20 Hz i 2 KHz). * mouseY: modifica el guany (entre -30 i 0 dB). * * tecla '1': selecciona el Mètode Suma 1. * tecla '2': selecciona el Mètode Suma 2. * tecla '3': selecciona el Mètode Suma 3. * tecla '4': selecciona el Mètode Suma 4. * */ // Importem els paquets necessaris de la biblioteca Minim import ddf.minim.*; import ddf.minim.ugens.*; import ddf.minim.analysis.*; // Definim els objectes Minim minim; Oscil myWave[]; Summer sum; // Aquest objecte permet sumar els 10 oscil·ladors Gain myGain; FFT fft; AudioOutput out; // freqFonamental és la freqüència de la fonamental del so que es genera float freqFonamental = 440.0f; // Valor del guany float dB; // Mètode per sumar els harmònics. Per defecte el 4 (tots els oscil·ladors // tenen la mateixa amplitud) int metSuma=4; // numHarm indica el nombre d'ones sinusoïdals que formarà el so generat int numHarm = 10; void setup() { size(740, 520); // Inicialitzem l'objecte de la classe Minim minim = new Minim(this); // Inicialitzem l'objecte sum, que permet sumar les 10 sinusoides sum = new Summer(); // Inicialitzem 'numHarm' oscil·ladors. myWave = new Oscil[numHarm]; // Creem els oscil·ladors i els connectem a l'objecte Summer for (int i = 0; i < numHarm ; i++) { myWave[i] = new Oscil(freqFonamental*(i+1), 0.1f, Waves.SINE ); myWave[i].patch(sum); } // Inicialitzem l'objecte gain. Per defecte el seu valor és 0 dB, que equival // a no aplicar canvis sobre l'amplitud myGain = new Gain(); // Construim l'objecte fft fft = new FFT(1024, 44100); // Fem servir el mètode getLineOut de l'objecte minim per inicialitzar un // objecte AudioOutput out = minim.getLineOut(); // Connectem el summer a l'objecte gain sum.patch(myGain); // Connectem el gain a la sortida d'àudio myGain.patch(out); } void draw() { background(0); stroke(255, 255, 0); strokeWeight(1); // Dibuixem la forma d'ona a la finestra de l'aplicació for (int i = 0; i < out.bufferSize() - 1; i++) { line( i, 50 - out.left.get(i)*50, i+1, 50 - out.left.get(i+1)*50 ); line( i, 150 - out.right.get(i)*50, i+1, 150 - out.right.get(i+1)*50 ); } // A la finestra de l'aplicació, mostrem informació sobre els valors actuals // dels harmònics, el guany i el mètode per sumar els harmònics utilitzat fill(200); text("Freq (Hz)", 548, 230); text("Amp", 664, 230); fill(255); for (int i=0; i<10; i++) { text("F(" + i + "): " + myWave[i].frequency.getLastValue(), 510, 250+(20*i)); text(myWave[i].amplitude.getLastValue(), 660, 250+(20*i)); } fill(0, 255, 255); text("Gain: " + dB + " dB", 510, 460); text("Mètode suma harmònics: " + metSuma, 510, 480); // Fem l'anàlisi fft sobre el buffer mix fft.forward(out.mix); // Dibuixem l'espectre a la finestra de l'aplicació // Multipliquem el valor de l'anàlisi (getBand) per 4 per visualitzar // millor l'espectre for (int i = 0; i < out.bufferSize() - 1; i++) { // Espectre en vermell stroke(255, 0, 0); line(i, height, i, height - fft.getBand(i)*4); } } // Des del teclat controlem el mètode per sumar els harmònics void keyPressed() { switch(key) { // Dent de serra case '1': for (int i = 0; i < numHarm; i++) { myWave[i].setAmplitude(0.2/(i+1)); } metSuma = 1; break; // Quadrada case '2': for (int i = 0; i < numHarm; i++) { myWave[i].setAmplitude((0.2/(i+1))*((i+1)%2)); } metSuma = 2; break; // Triangular case '3': for (int i = 0; i < numHarm; i++) { myWave[i].setAmplitude((0.2/sq(i+1))*((i+1)%2)); } metSuma = 3; break; // Tots els harmònics amb la mateixa amplitud case '4': for (int i = 0; i < numHarm; i++) { myWave[i].setAmplitude(0.1f); } metSuma = 4; break; default: break; } } void mouseMoved() { // Cada cop que el ratolí es mogui s'executa la funció mouseMoved() // Amb la posició horitzontal del punter del ratolí es controla la freqüencia // fonamental i els harmònics, i amb la posició vertical el guany dB = map(mouseY, 0, height, 0, -30); myGain.setValue(dB); float freq = map(mouseX, 0, width, 20, 2000); for (int i = 0; i < numHarm; i++) { myWave[i].setFrequency(freq*(i+1)); } }