SuperBerny escribió:Por otro lado estoy intendo crea un clon del popular juego Pong, ya lo tengo casi listo pero me falta añadirle sonido (una especie de beep cuando la bola rebota). He copiado esta parte del codigo de uLoader para generar el patron de sonido:
// sound pattern generator
for(n=0;n<2;n++)
{
int m,l;
switch(n)
{
case 0:
l=64;
for(m=0;m<2048;m++)
{
sound_effects[0][m]=((m) & l) ? 127 : -128;
if((m & 255)==0) l>>=1; if(l<16) l=16;
}
break;
case 1:
l=127;
for(m=0;m<2048;m++)
{
sound_effects[1][m]=((m) & 8) ? l : -l;
if((m & 7)==0) l--; if(l<0) l=0;
}
break;
}
}
pero me gustaria saber que es lo q estoy haciendo realmente.
Donde puedo encontrar mas informacion sobre esto?
Existe alguna utilidad similar al SpriteGen pero para crear efectos de sonido?
EDITO: Bueno, pues ya he descifrado los patrones creados por el codigo los podeis ver en forma de grafica en este
excel. Aun asi sigo con las mismas dudas que antes ¿como afecta un patro u otro al sonido?
Jejeje, eso es un generador de patrón de sonido al voleo, sin ajustar nada, salvo de oído.
Siento no haberte leído antes pero la verdad, es que no me paso por aquí hace mucho
Si quieres te explico un poco sobre la generación de sonido en sí, que sobre esa rutina guarra-salchichera improvisada
.
Bueno, allá voy:
El sonido se compone de una onda o serie de ondas a cierta frecuencia-as. La onda puede tener una determinada forma y en electrónica es corriente hablar de onda cuadrada, triangular o sinusoidal debido a como se representan gráficamente.
Las tarjetas de sonido y en este caso, el hardware de la Wii, utiliza un sistema de codificación de samples llamado PCM.
El PCM consiste en un numero que se traduce en una tensión de salida hacia al altavoz mediante un conversor digital a analógico.
Así pues, si tenemos un sample de 16 bits, tenemos un rango de -32768 - 0 - 32767 para representar el pico negativo, cero voltios y el pico positivo de la señal analógica. Lo mismo ocurriría a 8 bits, con -128 - 0 - 127. Básicamente el sample así codificado, establece la altura de la señal si la estuviéramos representando gráficamente.
Al mismo tiempo, tenemos un parámetro que se llama "sample rate", que basicamente es la "velocidad" a la que se irán interpretando los samples en formato PCM para producir sonido.
El sample rate del hardware de Wii está en 48 Khz, lo cual equivale a reproducir 48000 samples por segundo, pudiéndose utilizar otros sample rates mediante la asndlib utilizando un método de ensanchamiento o encogimiento de la señal, que básicamente consiste en suprimir samples o replicarlos, para adaptarlo a los 48Khz finales.
Por tanto, si quisiéramos representar un sonido en pantalla, tendríamos que el nivel del sample, establece la altura y el sample rate, se representaría horizontalmente.
Si quisiéramos representar una señal cuadrada a una determinada frecuencia, lo que habría que hacer es fijar un nivel positivo durante una serie de samples y luego fijar un nivel negativo por otra serie de samples. El número de samples a mantener el pico, vendría determinado por el sample rate de la señal, la frecuencia que queramos obtener, dividido entre dos para cada uno de los ciclos positivo y negativo.
Por ejemplo, si tenemos 48000 Hz de sample rate y queremos obtener una señal cuadrada de 500Hz, deberíamos dividir 48000/(500*2), lo cual nos daría que habría que generar 48 samples positivos (a 8 bits el maximo sería 127) y 48 samples negativos (a 8 bits el máximo sería -128) de forma cíclica para obtener dicha señal. Como veis el sistema es sencillo, en el caso de la onda cuadrada, aunque también es fácil de adivinar que un sample rate de 48Khz no es demasiado preciso para representar fielmente frecuencias altas.
Con 48Khz de sample rate, la máxima frecuencia representable serían 24Khz, puesto que para representar una señal necesitas un pico alto y otro bajo como mínimo y por tanto se necesitan dos samples para 24Khz (no tienen los picos porque estar por encima o por debajo de cero obligatoriamente, aunque se supone que una señal pura (sin componente continua o mezclada con otra seña) si debería representarse así). Una señal de 1Hz (inaudible), precisaría 24 mil samples positivos y otros 24 mil negativos
La señal cuadrada es un poco "dura" para el oído así que es mas corriente usar una señal sinusoidal. La función seno te puede ayudar a obtener esta característica forma. Por ejemplo en el caso de los 500Hz de antes, interpolando los valores de dicha función en 96 pasos (puesto que seno produce una curva positiva y luego una curva negativa, se obtendrían los samples necesarios dividiendo 48000/500= 96) desde 0 a 2*pi y multiplicando el valor por el "volumen" (de 0 a 127 para 8 bits o de 0 a 32767 para 16) de la onda.
Con estos sistemas obtenemos señales demasiado puras para el oído y tal vez queramos enriquecer el sonido. Una forma de hacerlo es mezclando diferentes ondas con frecuencias armónicas de igual forma que cuando en un teclado, en vez de pulsar la nota do, la combinamos con otras notas para obtener un acorde.
También nos puede interesar crear un efecto de reverberación (con retardar una señal unos 20 ms vale, o sea, que la señal volvería a empezar a esa distancia) o de eco. Incluso tal vez establecer efectos de ataque (haciendo que durante un tiempo, el "volumen" del sample se multiplique por una valor que crece de 0 a X y dividiéndolo después entre X) sostenimiento (lo dejamos tal cual) caída (parecido al ataque, solo que al revés, iríamos de X a 0, dividiendo el resultado entre X). Por cierto, cuidado al operar con enteros con no desbordar el resultado de las operaciones y evidentemente, todas las cifras de tiempo, habrá que traducirlas a samples (si sample rate 48Khz -> 48000 samples = 1 segundo luego 20 ms = 48000* 0.020 seg= 960 samples. Nuestra X podria ser 960 si por ejemplo, queremos hacer un decay de 20 ms)
Y bueno, ya solo queda mencionar la mezcla: el mezclado de dos samples consiste en sumar ambos valores y luego comprobar si excede de los límites y en caso afirmativo, ajustarlo al valor máximo de pico
Por ejemplo, si tenemos dos samples de 8 bits, uno con el valor 100 y otro con el valor 60, al sumarlos, excederán de 127, que es el valor máximo con signo que podemos almacenar con 8 bits. Por tanto, el resultado abría que ajustarlo a 127 como mucho. Esto provocaría un recorte de la señal, obviamente, provocando una distorsión de sonido... pero es la forma de hacerlo.
Por cierto, estamos hablando de señales monofónicas. Si la señal fuera estereofónica, habría que tener en cuenta que lo normal es que los samples izquierdo y derecho vayan intercalados corrientemente así: IDIDIDID... así pues, se necesitarían 96000 samples para 48Khz al tratarse de dos canales y habría que tener en cuenta ese entrelazamiento de los samples. (por cierto en Wii, si usais la DMA los samples se almacenan como DIDIDI... posiblemente porque se lean los samples como un dato de 32 bits big endian en el hardware)
Ya que estamos y como última cuestión, está el tema de adaptar el sample rate. Por ejemplo, si tengo un mp3 que se reproduce a 44100Hz, el sample rate es 44100, pero el hardware de la Wii lo hace a 48000.
¿Como podría adaptarlo? Pues parece obvio que hay que estirar la señal de alguna manera. La forma de hacerlo sería establecer una relación entre los dos sample rates de forma que tendríamos un contador que contaría de uno en uno para copiar el valor actual de la tabla que contiene los samples de 44100 a 48000. Sin embargo, solo se avanzaría al siguiente valor de la tabla de 44100 si al acumular 44100 en un contador rebasara o fuera igual al valor 48000, haciendo despues el ajuste pertinente del acumulador para que almacenase solo el valor que rebase.
Sería algo así:
void convertir_44100_a_48100(short *tab44100, short *tab48000, int numsamples)
{
int n;
int a=0,b=0,c=0;
// convertidor estéreo
for(n=0;n<numsamples;n++)
{
tab48000[a]=tab44100[b];
tab48000[a+1]=tab44100[b+1];
a+=2; // incrementamos dos samples
c+=44100;
if(c>=48000) {c-=48000; /* corrección de acumulador*/ b+=2;}
}
}
Esta rutina tiene varios inconvenientes:
- El primero es que está pensada solo para adaptar desde un sample rate menor o igual al que queremos convertir y por supuesto, en estereo.
- El segundo es que se limita a rellenar los "huecos" que resultan al estirar con el valor actual que señale la tabla de 44100hz, cuando sería mas correcto almacenar un valor interpolado (intermedio) entre el sample actual de la tabla 44100 y el siguiente para los "huecos". En el caso de 44100 a 48000, como solo vamos a encontrarnos un hueco cada cierto tiempo, bastaría con un solo valor intermedio, pero si estuviéramos convirtiendo un sample rate bastante inferior, habría un mayor número de huecos consecutivos y la rutina encargada de interpolar los valores, sería mas compleja (incluso se podría recurrir a otros algoritmos en lugar de usar una simple interpolación lineal)
- El tercero es que lo normal, es que el la "tabla" de 48Khz trabaje como un doble buffer en anillo y aquí estamos omitiendo algunos detalles necesarios.
Pero vamos, básicamente aquí tenéis todo lo necesario para trabajar y experimentar con el sonido, concentrado en un solo post