[PPA 07] Síntesis aditiva con tabla de datos.

Aquí va un patch en PureData que ilustra como funciona la síntesis
aditiva:

SintesisAditiva.pd

Las siguientes funciones muestran cómo se puede pre-llenar un buffer con
un periodo de una señal para después ser leído en el callback de audio.
Esto mejora dramáticamente la velocidad de computo de un sintetizador:
los cálculos se hacen de antemano y durante el callback solo hay
operaciones de acceso a memoria.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
//================//
//  Llenar Tabla  //
//================//
void normalizeTable(float *table, int length){
  int i;
  float maxAmp;
  float amp;
  for (i=0; i<length; i++) {
    amp = fabs(table[i]);
    if(amp>maxAmp)
      maxAmp = amp;
  }

  maxAmp = 1.0/maxAmp;
  for (i = 0; i < length; i++) {
    table[i] *= maxAmp;
    printf("%f\n",table[i]);
  }
}

float* fourierTable(int harms, float *amps, int length, float phase){
  float *table = malloc((length+2) * sizeof(float));
  memset(table,0,sizeof(float)*(length+2));

  phase *= PI*2.0f;

  float a;
  double w;
  int i;
  for (i=0; i<harms; i++) {
    int n;
    for (n=0; n<(length+2); n++) {
      a = amps ? amps[i] : 1.0f;
      w = (i+1)*(n*2*PI/length);
      table[n] += (float)(a*cos(w+phase));
    }
  }

  normalizeTable(table, length+2);
  return table;
}

float* sawTable(int harms, int length){
  float* amps = malloc(harms * sizeof(float));
  int i;
  for (i=0; i<harms; i++) {
    amps[i] = 1.0f / (i+1);
  }
  float* table = fourierTable(harms, amps, length, -0.25);
  free(amps);
  return table;
}

float* squareTable(int harms, int length){
  float* amps = malloc(harms * sizeof(float));
  memset(amps, 0, sizeof(float)*harms);
  int i;
  for (i=0; i<harms; i+=2) {
    amps[i] = 1.0f / (i+1);
  }
  float* table = fourierTable(harms, amps, length, -0.25);
  free(amps);
  return table;
}

float* triangleTable(int harms, int length){
  float* amps = malloc(harms * sizeof(float));
  memset(amps, 0, sizeof(float)*harms);
  int i;
  for (i=0; i<harms; i+=2) {
    amps[i] = 1.0f / ((i+1)*(i+1));
  }
  float* table = fourierTable(harms, amps, length, 0);
  free(amps);
  return table;
}

Aquí hay unas cuantas señales generadas con estas funciones:

Para leer los datos en el callback, se lée el buffer saltando cierto
número de muestras y de esta manera se puede controlar la frecuencia de
la señal generada:

1
2
3
4
5
6
7
8
9
foat incr = freq*length/sr;

for(int i=0; i<vecsize; i++){
  // truncated lookup
  output[i] = amp * table[(int)(*index)];  // index is a variable passed by reference to this function or a global var.
  *index += incr;
  while(*index >= length) *index -= length;
  while(*index < 0) *index += length;
 }

Fíjese que el indice con el que se lee la tabla se calcula como un
número de punto flotante (puede caer entre dos muestras) y se trunca a
un entero y de esta manera se escoge el valor de la muestra que está a
la izquierda del indice en la tabla. Esto introduce un error en la señal
generada y se puede corregir interpolando el valor que se devuelve. La
desventaja es que se introducen cálculos en el loop del callback (pero
son cálculos sencillos sigue siendo mucho más eficaz que calcular la
señal completa en el callback).

La expresión matemática para hacer interpolación lineal es
y = y1 + (y2 - y1) * (p - x1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
float incr = freq*length/sr;
float frac, pos, a, b;
phase = phase < 0 ? 1+phase : phase;
int offset = (int)phase*length%length;

for(int i=0; i<vecsize; i++){
  pos = *index + offset;

  //linear interpolation
  frac = pos - (int)pos;
  a = table[(int)pos];
  b = table[(int)pos+1];
  output[i] = amp*(a+frac(b-a));

  *index =+ incr;
  while(*index >= length) *index -= length;
  while(*index < 0) *index += length;
}

One thought on “[PPA 07] Síntesis aditiva con tabla de datos.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

*

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>