Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Crear y destruir el sonido en OpenAL sin aumentar el uso de memoria

Iniciado por Hans, 12 de Agosto de 2009, 04:24:35 PM

« anterior - próximo »

Hans

Audiere está tirado de usar pero tiene mil bugs (entre ellos que peta con multiprocesador), así que nos vamos a pasar a OpenAL. El problema es que tiene en nuestros juegos hay cerca de 300 sonidos y OpenAL permite reproducir muchos menos al mismo tiempo por temas de hardware. He pensado en destruir  y crear los sonidos según los necesite pero no encuentro una manera de hacerlo que no incremente la memoria cada vez.

He modificado el código de uno de los miles de tutoriales que he visto por ahí y que son casi clónicos. A ver si alguien me puede ayudar :P


#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <al.h>
#include <alc.h>
#include <alut.h>



class cSound
{
private:

    char* rutaSonido;

    ALuint Buffer; // Buffers to hold sound data.
    ALuint Source; // Sources are points of emitting sound.
    ALfloat SourcePos[3]; // Position of the source sound.
    ALfloat SourceVel[3]; // Velocity of the source sound.
    ALfloat ListenerPos[3]; // Position of the Listener.
    ALfloat ListenerVel[3]; // Velocity of the Listener.
    ALfloat ListenerOri[6]; // Orientation of the Listener. (first 3 elements are "at", second 3 are "up") // Also note that these should be units of '1'.
    // Variables to load into.

    ALenum format;
    ALsizei size;
    ALvoid* data;
    ALsizei freq;
    ALboolean loop;

public:

    cSound(char* ruta);

    ALboolean LoadALData(bool loadSources);
    void SetListenerValues();
    void KillALData();
    void LoadDefaultValues(void);
    void reloadData();

    void play();
    void stop();
    void pause();
};



#include "sound.h"


cSound::cSound(char* ruta)
{
    this->rutaSonido = ruta;

    LoadDefaultValues();
    // Initialize OpenAL and clear the error bit.

    alutInit(NULL, 0);
    alGetError();

    // Load the wav data.

    if (LoadALData(true) == AL_FALSE) printf("Error loading data.");

    SetListenerValues();
}


void cSound::play()
{
    alSourcePlay(Source);
}

void cSound::stop()
{
    alSourceStop(Source);
}

void cSound::pause()
{
    alSourcePause(Source);
}

/*
* ALboolean LoadALData()
*
* This function will load our sample data from the disk using the Alut
* utility and send the data into OpenAL as a buffer. A source is then
* also created to play that buffer.
*/
ALboolean cSound::LoadALData(bool loadSources)
{
    // Load wav data into a buffer.

    alGenBuffers(1, &Buffer);

    if (alGetError() != AL_NO_ERROR) return AL_FALSE;

    alutLoadWAVFile(rutaSonido, &format, &data, &size, &freq, &loop);
    alBufferData(Buffer, format, data, size, freq);
    alutUnloadWAV(format, data, size, freq);

    // Bind the buffer with the source.
    alGenSources(1, &Source);

    if (alGetError() != AL_NO_ERROR) return AL_FALSE;

    alSourcei (Source, AL_BUFFER,   Buffer   );
    alSourcef (Source, AL_PITCH,    1.0      );
    alSourcef (Source, AL_GAIN,     1.0      );
    alSourcefv(Source, AL_POSITION, SourcePos);
    alSourcefv(Source, AL_VELOCITY, SourceVel);
    alSourcei (Source, AL_LOOPING,  loop     );

    // Do another error check and return.

    if (alGetError() == AL_NO_ERROR) return AL_TRUE;

    return AL_FALSE;
}


void cSound::reloadData()
{
    KillALData();


}


/*
* void SetListenerValues()
*
* We already defined certain values for the Listener, but we need
* to tell OpenAL to use that data. This function does just that.
*/
void cSound::SetListenerValues()
{
    alListenerfv(AL_POSITION, ListenerPos);
    alListenerfv(AL_VELOCITY, ListenerVel);
    alListenerfv(AL_ORIENTATION, ListenerOri);
}



/*
* void KillALData()
*
* We have allocated memory for our buffers and sources which needs
* to be returned to the system. This function frees that memory.
*/
void cSound::KillALData()
{
    alDeleteBuffers(1, &Buffer);
    alDeleteSources(1, &Source);
    free(&Buffer);
    alutExit();
}


void cSound::LoadDefaultValues(void)
{
    SourcePos[0] = 0.0; // Position of the source sound.
    SourcePos[1] = 0.0; // Position of the source sound.
    SourcePos[2] = 0.0; // Position of the source sound.

    SourceVel[0] = 0.0; // Velocity of the source sound.
    SourceVel[1] = 0.0; // Velocity of the source sound.
    SourceVel[2] = 0.0; // Velocity of the source sound.

    ListenerPos[0] = 0.0; // Position of the Listener.
    ListenerPos[1] = 0.0; // Position of the Listener.
    ListenerPos[2] = 0.0; // Position of the Listener.

    ListenerVel[0] = 0.0; // Velocity of the Listener.
    ListenerVel[1] = 0.0; // Velocity of the Listener.
    ListenerVel[2] = 0.0; // Velocity of the Listener.

    ListenerOri[0] = 0.0; // Orientation of the Listener. (first 3 elements are "at", second 3 are "up") // Also note that these should be units of '1'.
    ListenerOri[1] = 0.0; // Orientation of the Listener. (first 3 elements are "at", second 3 are "up") // Also note that these should be units of '1'.
    ListenerOri[2] = -1.0; // Orientation of the Listener. (first 3 elements are "at", second 3 are "up") // Also note that these should be units of '1'.
    ListenerOri[3] = 0.0; // Orientation of the Listener. (first 3 elements are "at", second 3 are "up") // Also note that these should be units of '1'.
    ListenerOri[4] = 1.0; // Orientation of the Listener. (first 3 elements are "at", second 3 are "up") // Also note that these should be units of '1'.
    ListenerOri[5] = 0.0 ; // Orientation of the Listener. (first 3 elements are "at", second 3 are "up") // Also note that these should be units of '1'.
}




Cada vez que necesite reproducir un sonido pensaba hacer algo en plan:

sonido[MAX_SONIDOS-1]->KillALData();
sonido[MAX_SONIDOS-1]->LoadALData(false);
sonido[MAX_SONIDOS-1]->play();code]

Prompt

Cita de: Hans en 12 de Agosto de 2009, 04:24:35 PM
Audiere está tirado de usar pero tiene mil bugs (entre ellos que peta con multiprocesador), así que nos vamos a pasar a OpenAL. El problema es que tiene en nuestros juegos hay cerca de 300 sonidos y OpenAL permite reproducir muchos menos al mismo tiempo por temas de hardware.


No tengo tiempo ahora de mirarme esto con detalle, pero yo implementé todo un moto de sonido para mi empresa en OpenAL, estoy muy orgulloso del resultado. Implementé double buffering, streaming de wavs (de ogg viene por ahi un codigo) y soporte multi-hilo, thread-safe por tanto.

Si selecionas el device de SOFTWARE podrás tener todos los sonidos que quieras. En el mundo no WINDOWS, puedes tener más de 32 sonidos acelerados por hardware, los drivers se encargan de esto. Pero claro OpenAL es un wrapper de medio nivel de DirectSound, DirectSound3D, y los equivalentes en Linux por ejemplo, ALSA, OSS etc... lo que quiero decir es que solo en WINDOWS no puedes tener más de 32 sonidos activos que sean acelerados por hardware, con lo cual debes hacer pooling para poder tener los que te de la gana acelerados por hardware. Si tienes 100 sonidos activos, activas en OpenAL de 32 en 32 y los procesas. Así que si dices que OpenAL permite "muchos menos" que una lib que lo usa, ese concepto falla en algo no? pegale un repaso a la documentación porque te va a ahorrar disgustos. Sino selecciona el device por software.

Cita de: Hans en 12 de Agosto de 2009, 04:24:35 PM
He pensado en destruir  y crear los sonidos según los necesite pero no encuentro una manera de hacerlo que no incremente la memoria cada vez.

Si no encuentras manera significa que el que la está liando eres tu. Ya que OpenAL no se encarga de destruir y crear punteros, solo de manejarlos. Revisa tu código.
Debes hacerte una clase de test para esto, donde crees y destruyas tu motorcillo de sonido un monton de veces y ver si crece la memoria o se mantiene. Lo mismo para crear el motor, cargar todos los sonidos y destruirlo. Así localizarás el problema.

No obstante debes poner algo de lógica a esto y tener un máximo de memoria para el motor de sonido. Es decir que puedas tener 100 sonidos de menos de X megas o TIEMPO en memoria a la vez, si no debe salir algún aviso. Es imposible que tengas más de 100 sonidos sonando al mismo tiempo, qué locura (en un videojuego) como mucho podrás tener no se 50? y otros 50 "en caché" para activarlos rápidamente.

Si el sonido ocupa más de X megas o TIEMPO debes hacer streaming, ya que sino vas a comerte toda la memoria del PC y la tarjeta de sonido.

Tips and Tricks finalizado, seguro que te sirven :)

H-K

Tal y como haces ahí, si reproduces dos sonidos iguales, los vas a cargar dos veces, todo un desperdicio oiga :P

En mi wrapper de OpenAL tengo clases para los source objects y para los sonidos. Y para reproducir, por una parte tengo un vector de sources, creados al inicio del programa, y por otra parte una lista de sonidos. Cuando quiero reproducir un sonido, lo busco en la lista, y si no está, lo cargo y lo asocio al primer source libre o que tenga asociado uno de menor prioridad.

Por lo que dice Prompt, yo creo que tiene más sentido procesar los sonidos por prioridades en lugar de por bloques. Así, podrás tener activos los que haga falta, pero sonando sólo los que se puedan reproducir simultáneamente, que serán los que tengan mayor prioridad. Por ejemplo, la música, diálogos y sonidos de eventos especiales tendrían la máxima prioridad, y a los sonidos "normales" se les asignaría una prioridad descendente en función de la distancia al oyente (esto habría que refinarlo un poco, ya que un sonido de una explosión a 10 metros debería tener más prioridad que el sonido de una piedrecita al chocar con el suelo a 1 metro, pero como ejemplo sirve)

Esto que digo no es la verdad universal ni mucho menos, todo está abierto a debate, claro está :)

Por cierto Prompt, ¿seguro que en ALSA/OSS hay aceleración por hardware? Yo estaba convencido de que fuera de Windows+DS3D o XFi/Audigy todo iba por software. Y en el Generic software device se pueden crear hasta 256 sources, o al menos así era con la última versión que probé. De todas maneras, yo creo que tantos sonidos simultáneos son del todo innecesarios, con 16-32 a la vez ya tienes suficiente. Más es ansia.

Otra cosa, hay una implementación aparte de la de Creative que funciona por software en Windows, Mac y Linux, y tira bastante bien. Soporta más de dos canales en Windows, cosa que por ejemplo el software device de Creative no hace, y te olvidas de rollos raros con problemas que pueda dar el hardware device. Se llama OpenAL soft y la puedes coger de aquí: http://kcat.strangesoft.net/openal.html. Lo único "malo" que tiene es que te la tienes que compilar tú mismo.
"It's not who I am underneath, but what I do that defines me"

Prompt

Cita de: H-K en 13 de Agosto de 2009, 03:06:32 PM
Por cierto Prompt, ¿seguro que en ALSA/OSS hay aceleración por hardware? Yo estaba convencido de que fuera de Windows+DS3D o XFi/Audigy todo iba por software. Y en el Generic software device se pueden crear hasta 256 sources, o al menos así era con la última versión que probé. De todas maneras, yo creo que tantos sonidos simultáneos son del todo innecesarios, con 16-32 a la vez ya tienes suficiente. Más es ansia.

Si existe un driver en condiciones yo creo que tendrá esa posibilidad permitida. No te lo puedo asegurar porque ahora no tengo como, pero afirmar lo contrario es como decir que el sonido en Linux va por software siempre.

En las placas OMAP3, iPhone, OpenPandora, Nokia, Pal Pre... tiene un chip para DSP y tienen que hacer un driver para ALSA o OSS o el que sea, para que al usar un wrapper como OpenAL el sonido pase a reproducirse por hardware. Esto es muy importante sobre todo en máquinas que tienen una CPU regular y necesitan un chip de apollo con instrucciones más potenciadas para quitarle la carga al micro principal.

Mars Attacks


Hans

ufff, cuánto conocimiento y yo que quería algo simple  :P Al final me he construído un servidor de sonido con conexión local que se ejecuta de fondo y reproduce los sonidos que mis juegos le piden pasando la ruta y algunas cosillas más. Tenía prisa y como ya he podido descargar los sonidos y volver a cargarlos sin problemas eso me ha venido bien para solventar problemas de contexto de OpenAL que no me daba tiempo a comprender.

Prometo leerlo todo, gracias por responder :P






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.