Anuncios

Bienvenidos sean a este post, hoy veremos como implementar esta libreria.

Anuncios

Hoy veremos como agregar una nueva extension pero pensado para manejar todo lo relacionado al sonido.

Anuncios

Para ello, primero abriremos a Visual Studio y elegiremos la opcion de Clonar Repositorio, lo cual nos abrira la siguiente ventana

Anuncios

En la ubicacion del repositorio, deben agregar la siguiente URL:

https://github.com/libsdl-org/SDL_mixer

Anuncios

Y la Ruta de acceso va a ser donde copiaremos el repositorio anterior, les recomiendo dejar el predeterminado (como se ve en la imagen) pero sino pueden especificar a donde sea necesario ubicarlo. Le dan al boton Clonar y comenzara con el proceso, y una vez finalizado nos quedara en el explorador de soluciones de la siguiente manera:

Anuncios

Con esto ya tenemos nuestra copia local lista para ser implementada en nuestro proyecto Juego. Sino lo poseen les dejo un link para descargarlo:

Proyecto Juego

Anuncios

Descarguen el archivo y extraigan el contenido en el PC. Lo unico que deben hacer es revincular a SDL y SDL_Image. Para ello, les recomiendo este post donde comento como hacerlo para SDL y este otro post donde hago lo mismo para SDL_Image. Una vez que este todo funcionando correctamente, podemos continuar.

Anuncios

Antes de pasar a trabajar con nuestro codigo, necesitamos implementar a SDL_Mixer. Para ello, iremos a nuestra Solucion. Hacemos un click con el boton derecho, iremos a Agregar, luego a Proyecto existente. En la nueva ventana que nos abrira, deben ir hasta a SDL_mixer.vcxproj y seleccionarlo como se ve en la siguiente imagen

Anuncios

Este se encuentra en la carpeta VisualC, les dejo el path predeterminado de donde pueden ubicar a este archivo:

C:\Users\<usuario local>\source\repos\SDL_mixer\VisualC

Anuncios

Nuestro siguiente paso sera agregarlo a nuestro proyecto. Hacemos click con el boton derecho pero ahora sobre el proyecto (Juego) y deben seleccionar Agregar, luego Referencia. Esto nos abrira una nueva ventana, en la cual seleccionaremos a SDL3_Mixer. Tal como se ve en la siguiente imagen

Anuncios

Presionan el boton Aceptar y esto procede a agregarlo como nueva referencia, y nos quedara de la siguiente manera:

Anuncios

Solo nos resta una ultima modificacion, como es agregar al directorio include de la nueva extension. Para ello, haremos click con el boton derecho sobre el proyecto pero elegiremos Propiedades. Nos abrira una nueva ventana, y en ella debemos ir primero a Propiedades de configuracion, luego a C/C++ y por ultimo a General. En el panel del lado derecho, elegiremos a la opcion Directorio de inclusion adicionales. Sin eliminar el contenido anterior, debemos agregar a nuestro directorio include. Les paso como es el path desde el directorio predeterminado:

C:\Users\<usuario local>\source\repos\SDL_mixer\include

Anuncios

Lo agregan, recuerden que deben separarlo de los paths anteriores mediante un punto y coma (;), tal como se ve en la siguiente imagen

Anuncios

Si observan en la imagen, reemplace la parte donde esta el directorio de perfiles (c:\users\<usuario local>) por la variable de entorno llamada %USERPROFILE% porque contiene ese valor. Los signos de porcentaje son para indicar que es del S.O. Con esto realizado, solo nos resta un par de modificacioes mas.

Anuncios

Para poder compilar correctamente a SDL3_Mixer debemos agregar el proyecto timidity. Para ello, sobre la solucion deben hace click con el boton derecho y seleccionar Agregar, luego Proyecto existente. Esto abrira una nueva ventana y en ella deben ir al directorio timidity en VisualC. Dentro deben seleccionar a timidity.vcxproj como se ve en la siguiente imagen

Anuncios

Presionan Abrir y nos agregara el proyecto a la lista de los existentes en nuestra solucion. En este proyecto, deben hacer click con el boton derecho y seleccionar Propiedades. Esto nos abrira una nueva ventana, en ella deben ir a Propiedades de configuracion->C/C++->General. En el panel del lado derecho deben seleccionar a la opcion Directtorios de inclusion adicionales y agregar el directorio include pero de SDL. Les dejo como es el path para la version predeterminada:

C:\Users\<usuario local>\source\repos\SDL\include

Anuncios

Este se debe agregar a los ya existentes, no deben eliminar ninguno anterior. Debe quedar como se ve en la siguiente imagen

Anuncios

Le dan Aceptar, se cerrara la ventana y ya tenemos todo listo para poder utilizar nuestra libreria pero no va a funcionar porque nos falta realizar una serie de modificaciones mas.

Anuncios

Las siguientes modificaciones son simplemente para poder habilitar los dos decodificadores que necesitaremos para nuestro codigo. Estos cambios los debemos realizar en SDL3_Mixer, deben ir a este proyecto. Abran la carpeta con el nombre de Sources y ahi deben buscar a decoder_stb_vorbis.c. Abran ese archivo y busquen la siguiente linea:

#ifdef DECODER_OGGVORBIS_STB
Anuncios

Antes de esta linea deben agregar la siguiente linea:

#define DECODER_OGGVORBIS_STB
Anuncios

Esto permitira que el condicional se cumple, y permita que se defina todas las propiedades y funciones para poder decodificar los archivos de formato OGG, los cuales usaremos para la musica de nuestro juego. En el archivo, las lineas deben quedar asi:

#define DECODER_OGGVORBIS_STB
#ifdef DECODER_OGGVORBIS_STB
Anuncios

Nuestra siguiente modificacion sera similar a esta pero en decoder_wav.c. Al igual que antes volveremos a buscar la siguiente linea:

#ifdef DECODER_WAV
Anuncios

Y agregaremos la siguiente linea antes de la anterior:

#define DECODER_WAV
Anuncios

Al igual que en el caso anterior, esto nos habilitara las propiedades y funciones para decodificar los archivos WAV. Estos seran usados para los efectos en el juego. Al igual que vimos antes, esta nueva modificacion debe quedar asi:

#define DECODER_WAV
#ifdef DECODER_WAV
Anuncios

Nuestra ultima modificacion, sera en SDL_mixer.c. Al inicio del archivo, agregaremos las siguientes dos lineas:

#define DECODER_WAV
#define DECODER_OGGVORBIS_STB
Anuncios

Estas dos lineas son basicamente las encargadas de agregar estos decodificadores para poder ser utilizados. Con todo esto realizado, nuestro primer paso en el proyecto sera crear una nueva clase llamada ManejarSonido. Una vez creado, iremos al archivo de encabezado y agregaremos el siguiente codigo:

ManejarSonido.h
#pragma once
#ifndef __ManejarSonido__
#define __ManejarSonido__
#include <SDL3_mixer/SDL_mixer.h>
#include <iostream>
#include <string>
#include <map>

enum tipo_sonido {
	MUSICA = 0,
	SFX = 1
};

class ManejarSonido
{
public:
	static ManejarSonido* instanciar();
	bool carga(std::string, std::string, tipo_sonido);
	void playSonido(std::string, int);
	void playMusica(std::string, int);

private:
	static ManejarSonido* e_pInstanciar;
	std::map<std::string, MIX_Audio*> m_sfxs;
	std::map<std::string, MIX_Audio*> m_musica;
	ManejarSonido();
	~ManejarSonido();
	ManejarSonido(const ManejarSonido&);
	ManejarSonido& operator=(const ManejarSonido&);
	MIX_Mixer* m_mixer;
	SDL_AudioSpec m_espec;

};

#endif // !__ManejarSonido__

typedef ManejarSonido Elsonido;
Anuncios
Anuncios

En este archivo declaramos todo lo necesario para poder manejar los sonidos. Primero definimos un enum para identificar si va a ser un sonido de efecto (SFX) o de musica. En la parte privada, tenemos dos colecciones que seran para almacenar todos los efectos de sonido y musica que usaremos. Sobre esto, hablaremos en un momento, su identificacion sera un texto y el objeto relacionado es un MIX_Audio. Este es el tipo de objeto que nos permite manejar el audio. A diferencia de las versiones anteriores, no es necesario diferenciar entre los tipos de datos, antes eran Mix_Music y Mix_Chunk para la musica y efectos respectivamente, y por eso usamos a MIX_Audio para almacenarlos. Tambien tenemos el constructor para utilizarlo como singleton. Tenemos al destructor, una sobrecarga del constructor y de un operador. Asi como tambien, el puntero que usaremoss para la instancia del singleton. Y finalmennte, dos objetos para trabajar con la musica (m_mixer) y otro para almacearr las especificaciones del audio (m_espec).

Anuncios

En la parte publica, tenemos a la funcion clasica para instanciar, asi como tambien una encargada de cargar al archivo de musica (para este codigo) pero tambien se usara para los arrchivos de efectos. Y dos funciones mas para reproducir la musica y los efectos. Si bien, podemos usar una sola funcion por un tema de comodidad y prolijidad las separaremos en dos funciones. Con todo esto realizado, lo siguiente sera agregar el siguiente codigo en el archivo .cpp:

ManejarSonido.cpp
#include "ManejarSonido.h"

ManejarSonido* ManejarSonido::e_pInstanciar;

ManejarSonido* ManejarSonido::instanciar() {
	if (e_pInstanciar == 0) {
		e_pInstanciar = new ManejarSonido();
		return e_pInstanciar;
	}
	return e_pInstanciar;
}

ManejarSonido::ManejarSonido() {
	m_espec.format = SDL_AUDIO_F32;
	m_espec.freq = 44100;
	m_espec.channels = 2;
	if (!MIX_Init()) {
		std::cout << "Algo fallo" << SDL_GetError() << std::endl;
	}
	else {
		m_mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &m_espec);
	}
}
ManejarSonido::ManejarSonido(const ManejarSonido& sonido) {}
ManejarSonido::~ManejarSonido() {
	MIX_DestroyMixer(m_mixer);
}

bool ManejarSonido::carga(std::string archivo, std::string id, tipo_sonido tipo) {

	if (tipo == MUSICA) {
		MIX_Audio* pMusica = MIX_LoadAudio(m_mixer, archivo.c_str(), true);
		if (pMusica == 0) {
			std::cout << "No se pudo cargar Musica: ";
			std::cout << SDL_GetError() << std::endl;
			return false;
		}
		m_musica[id] = pMusica;
		return true;
	}
	else if (tipo == SFX) {
		MIX_Audio* pEfecto = MIX_LoadAudio(m_mixer, archivo.c_str(), true);
		if (pEfecto == 0) {
			std::cout << "No se pudo cargar SFX: ";
			std::cout << SDL_GetError() << std::endl;
			return false;
		}
		m_sfxs[id] = pEfecto;
		return true;
	}
	return false;
}

void ManejarSonido::playMusica(std::string id, int bucle) {
	SDL_PropertiesID props = SDL_CreateProperties();
	MIX_Track* track = MIX_CreateTrack(m_mixer);
	MIX_SetTrackAudio(track, m_musica[id]);
	
	SDL_SetNumberProperty(props,MIX_PROP_PLAY_LOOPS_NUMBER, bucle);
	MIX_PlayTrack(track, props);
}
void ManejarSonido::playSonido(std::string id, int bucle) {
	MIX_PlayAudio(m_mixer, m_sfxs[id]);
}
Anuncios
Anuncios

La primera linea es para definir a e_pInstanciar porque lo que hicimos en el archivo de encabezado es solamente una declaracion. Lo siguiente es la definicion de la funcion para crear el singleton. Esta al igual que siempre, chequea si en e_pInstanciar posee algo. Si no es asi, generamos un nuevo objeto y lo almacenamos ahi para devolver a este. A partir de esto, no se cumple esta condicion y siempre devolvemos la misma instancia. La siguiente definicion es el constructor basico o predeterminado, donde estableceremos las propiedades del archivo y estas las almacenaremos en m_espec. Lo siguiente es chequear si se inicio correctamente a la libreria. Si no es asi, mostraremos un mensaje con el error. En cambio, si se inicio correctamente definiremos al mixer. Este es el encargado de manejar a nuestra placa de sonido, para hacerla facil usamos a SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK (este toma la placa del S.O) y luego pasamos los parametros especificados anteriormente. Con nuestro mixer creado podemos continuar con el resto.

Anuncios

Definimos al otro constructor en blanco, y tambien al destructor para destruir a nuestro mixer. La siguiente definicion es una de las mas importantes, porque sera el encargado de cargar el archivo de audio. Este recibe tres argumentos; el primero sera para la ubicacion del archivo, el segundo sera el id de identificacion y el ultimo sera el tipo. El segundo sera el id que usaremos para identificarlo en las colecciones para los efectos y musica. El ultimo argumento sera para indicar en que coleccion lo agregaremos.

Anuncios

En el bloque, tenemos un condicional donde verifica si el tipo es MUSICA. En caso de ser verdadero, generamos un objeto de MIX_Audio donde mediante MIX_LoadAudio cargaremos al archivo de sonido. para ello primero pasamos el mixer. Luego pasamos la ubicacion del archivo, el c_str es para poder pasarlo correctamente, y el ultimo sera para indicar que lo pre-decodifique. El siguiente condicional verifica si hubo algun error a cargar lo anterior. En caso de ser verdadero muestra un mensaje indicando la falla. En caso contrario, ignora este condicional y almacena el objeto anterior en la coleccion de musica y usamos el id recibido para identificarlo, y luego devolver un true.

Anuncios

Si el valor recibido es SFX procede a hacer lo mismo que antes pero ahora al final lo agrega en la otra coleccion. Sino se cumplio ninguna condicion de las anteriores, procedemos a devolver un false para indicar que no se cargo nada. Lo siguiente es la definicion de las dos funciones encargadas de reproducir el sonido.

Anuncios

La funcion llamada playSonido es la que usaremos para reproducir a nuestros efectos. Para ello, recibimos dos argumentos. El primero es el id que usaremos para buscar en la coleccion de efectos, el segundo sera para establecer la cantidad de veces que se repetira. Para este caso, no se utilizara porque son solo efectos. En el bloque, usamos a MIX_PlayAudio para reproducirlo y para ello pasamos primero el mixer y luego el objeto obtenido de la coleccion. Esta es la version simple para reproducirlo porque no necesitamos aplicar ningun efecto o propiedad.

Anuncios

En cambio, la funcion playMusica es un poco mas compleja pero mas fructifera. Igual que en la funcion anterior recibe dos argumentos y son para lo mismo. Lo primero que haremos es crear un objeto para las propiedaades que aplicaremos luego. El siguiente objeto es el track que usaremos para reproducir, pero por que no usar lo mismo que en la funcion anterior? Porque de esta manera podemos aplicar un loop, efecto de salida y otros, y como argumento le pasamos el mixer para manejarlo. Lo siguiente es establecer el audio al track anterior. Seguido a esto, tomamos el objeto de las propiedades que creamos al inicio, y le modificamos el valor a esa propiedad con el segundo argumento recibido. Los posibles valores pueden ser:

  • 0, para no repetir
  • n, para repetir la cantidad de veces pasadas. p.e. si pasas 10 lo hara 10 veces
  • -1, lo repite infinitamente
Anuncios

Por ultimo, utilizamos a MIX_PlayTrack para reproducir el track generado anteriormente. Y como segundo argumento le pasamos las propiedades creadas anteriormente para que se apliquen sobre la cancion que pasamos al track. Con esto, tenemos a nuestra clase lista para ser utilizada.

Anuncios

Nuestro siguiente paso, sera habilitarlo para el juego. Lo primero que debemos hacer es ir a Juego.h y agregaremos la siguiente linea al inicio del archivo:

#include "ManejarSonido.h"
Anuncios

Esto es para poder acceder a todo lo creado anteriormente. Nuestro siguiente paso sera modificar la funcion inicio en Juego.cpp. En esta agregaremos las siguientes lineas antes de la carga del estado de menu:

Elsonido::instanciar()->carga("assets/juego.ogg", "musica", MUSICA);
Elsonido::instanciar()->playMusica("musica", -1);
Anuncios

La primer linea se encarga de cargar la cancion, y la segunda se encarga de reproducirla. Observen como pasamos al id para que haga todo lo comentado anteriormente. Y como valor pasamos a -1 para que se repita infinitamente. Antes de probarlo, les dejo el archivo de musica para descargarlo:

Anuncios

Este archivo se debe copiar en el directorio assets, con esto realizado ahora si pueden pasar a probarlo como se ve en el siguiente video::

Anuncios

Como pueden ver ahora nuestro juego tiene musica y podemos agregar mas temas relacionados a este pero eso sera un tema que veremos en el proximo proyecto donde aplicaremos todo lo visto hasta ahora y el siguiente paso para crear nuestro primer juego.

Anuncios

En resumen, hoy hemos visto a SDL_Mixer, que es, para que sirve, como se implementa, asi como tambien habilitarlo, y los decodificadores necesarios, hemos creado una clase encargada de manejar los sonidos, asi como tambien lo hemos aplicado a nuestro juego. Espero les haya sido de utilidad y les dejo un link a GitHub donde estan los codigos creados hoy:

SDL_Mixer / GitHub

Les dejo algunas de mis redes sociales para seguirme o recibir una notificacion cada vez que subo un nuevo post:

Anuncios
pp258

Donación

Es para mantenimento del sitio, gracias!

$1.50