commit
1c28d17eb3
@ -0,0 +1,8 @@
|
||||
#ifndef PRINTER_INCLUDED
|
||||
#define PRINTER_INCLUDED
|
||||
|
||||
#include <model/Coord.h>
|
||||
|
||||
void printCoord(const Coord coord);
|
||||
|
||||
#endif //PRINTER_INCLUDED
|
@ -0,0 +1,106 @@
|
||||
/**
|
||||
* \file ArrayUtils.h
|
||||
* \brief This file define a macro used to generate dynamic array structs and associated functions
|
||||
* \author Martin Rouault
|
||||
* \date 3/01/2022
|
||||
*/
|
||||
|
||||
#ifndef ARRAY_UTILS_INCLUDED
|
||||
#define ARRAY_UTILS_INCLUDED
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
/**
|
||||
* \brief Generate a dynamic array for type T
|
||||
* If you want an int array use
|
||||
* GENERATE_DYNAMIC_ARRAY(int)
|
||||
|
||||
* If you miss a function, write it with a type as int first then rewrite it in the macro
|
||||
* You will need to follow function naming convetions to avoid multiple functions with the same name
|
||||
* Functions in header must be marked as inline
|
||||
* Don't forget antislashes
|
||||
*/
|
||||
#define GENERATE_DYNAMIC_ARRAY(T) \
|
||||
struct array_##T { \
|
||||
T* elems; \
|
||||
size_t arraySize; \
|
||||
size_t arraySpace; \
|
||||
}; \
|
||||
\
|
||||
/*Contruct an empty array*/\
|
||||
inline struct array_##T array_##T##_Create() { \
|
||||
struct array_##T array = {.elems=NULL, .arraySize=0, .arraySpace=0}; \
|
||||
return array; \
|
||||
} \
|
||||
\
|
||||
/*Free an array*/ \
|
||||
inline void array_##T##_Free(struct array_##T* array) { \
|
||||
free(array->elems); \
|
||||
array->elems = NULL; \
|
||||
array->arraySize = 0; \
|
||||
array->arraySpace = 0; \
|
||||
} \
|
||||
\
|
||||
/*Reserve space for an array*/\
|
||||
inline void array_##T##_Reserve(struct array_##T* array, const size_t space) { \
|
||||
array->arraySpace = space; \
|
||||
array->elems = realloc(array->elems, sizeof(T)*(array->arraySpace)); \
|
||||
if (array->elems == NULL) exit(errno); \
|
||||
} \
|
||||
\
|
||||
/*Fit space to size for an array*/\
|
||||
inline void array_##T##_FitToSize(struct array_##T* array) { \
|
||||
array->arraySpace = array->arraySize; \
|
||||
array->elems = realloc(array->elems, sizeof(T)*(array->arraySpace)); \
|
||||
if (array->elems == NULL) exit(errno); \
|
||||
} \
|
||||
\
|
||||
/*Add an element to an array*/\
|
||||
inline void array_##T##_AddElement(struct array_##T* array, const T element) { \
|
||||
++(array->arraySize); \
|
||||
if (array->arraySize > array->arraySpace) { \
|
||||
++(array->arraySpace); \
|
||||
array->elems = realloc(array->elems, sizeof(T)*(array->arraySpace)); \
|
||||
if (array->elems == NULL) exit(errno); \
|
||||
} \
|
||||
\
|
||||
array->elems[array->arraySize - 1] = element; \
|
||||
} \
|
||||
\
|
||||
/*Remove an element from an array*/\
|
||||
inline bool array_##T##_RemoveElement(struct array_##T* array, const T element, bool (*areEqual)(const T, const T)) { \
|
||||
for (size_t i=0; i<array->arraySize; ++i) { \
|
||||
if (areEqual(array->elems[i], element)) { \
|
||||
for (;i<array->arraySize-1; ++i) { \
|
||||
array->elems[i] = array->elems[i+1]; \
|
||||
} \
|
||||
--(array->arraySize); \
|
||||
return true; \
|
||||
} \
|
||||
} \
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
/*Check if an array contains an element*/\
|
||||
inline bool array_##T##_Contains(const struct array_##T* const array, const T element, bool (*areEqual)(const T, const T)) { \
|
||||
for (size_t i = 0; i < array->arraySize; i++) { \
|
||||
if (areEqual(array->elems[i], element)) { \
|
||||
return true; \
|
||||
} \
|
||||
} \
|
||||
return false; \
|
||||
}\
|
||||
\
|
||||
/*Apply a function to each element in the array*/\
|
||||
inline void array_##T##_Foreach(const struct array_##T* const array, void (*func)(const T)) { \
|
||||
for(size_t i = 0; i<array->arraySize; ++i) { \
|
||||
func(array->elems[i]);\
|
||||
}\
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif //ARRAY_UTILS_INCLUDED
|
@ -1,23 +0,0 @@
|
||||
/**
|
||||
* \file Audio.h
|
||||
* \brief Audio management
|
||||
* \author Théotime Maillarbaux
|
||||
* \date 13/12/2021
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_H
|
||||
#define AUDIO_H
|
||||
|
||||
#include <SDL2/SDL_mixer.h>
|
||||
|
||||
/**
|
||||
* \brief Fades out a music and plays another one.
|
||||
* \param[in] music A pointer to the Mix_Music struct used to play the current track
|
||||
* \param[in] path The path to the new track to be played
|
||||
* \warning The program won't know if it failed.
|
||||
*
|
||||
* This function creates a thread that will detach on its own completion.
|
||||
*/
|
||||
void switchMusic(Mix_Music* music, char path[]);
|
||||
|
||||
#endif // AUDIO_H
|
@ -0,0 +1,126 @@
|
||||
/**
|
||||
* \file AudioHandler.h
|
||||
* \brief Audio management (music and sound effects)
|
||||
* \author Théotime Maillarbaux
|
||||
* \date 13/12/2021
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_H
|
||||
#define AUDIO_H
|
||||
|
||||
#include <SDL2/SDL_mixer.h>
|
||||
#include <SDL2/SDL_thread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* Macro used to generate enums elements and paths for musics.
|
||||
* It should take either #MACRO_MUSIC_ENUM_GEN(E) or #MACRO_TO_MUSIC_PATH(P)
|
||||
* as parameter.
|
||||
*
|
||||
* If new audios are added to the program, they should be added here.
|
||||
* \sa #MACRO_FOR_ALL_SFX(E)
|
||||
*/
|
||||
#define MACRO_FOR_ALL_MUSICS(M) \
|
||||
M(base_tardi) \
|
||||
M(testMus)
|
||||
|
||||
/**
|
||||
* Macro used to generate enums elements and paths for sound effects.
|
||||
* It works the same way as #MACRO_FOR_ALL_MUSICS(M).
|
||||
* \sa #MACRO_FOR_ALL_MUSICS(M)
|
||||
*/
|
||||
#define MACRO_FOR_ALL_SFX(M) \
|
||||
M(testClick)
|
||||
|
||||
/**
|
||||
* Macro used to generate the entries for the musics in #EnumAudios.
|
||||
* It should be used in #MACRO_FOR_ALL_MUSICS(M) as a parameter.
|
||||
* Each entry of #MACRO_FOR_ALL_MUSICS(M) will be converted as MUSIC_name.
|
||||
* \sa #MACRO_SFX_ENUM_GEN(E)
|
||||
*/
|
||||
#define MACRO_MUSIC_ENUM_GEN(E) MUSIC_##E,
|
||||
|
||||
/**
|
||||
* Macro used to generate the entries for the SFX in #EnumAudios.
|
||||
* It works the same way as #MACRO_MUSIC_ENUM_GEN(E), except the entries start with SFX_
|
||||
* instead of MUSIC_.
|
||||
* \sa #MACRO_MUSIC_ENUM_GEN(E).
|
||||
*/
|
||||
#define MACRO_SFX_ENUM_GEN(E) SFX_##E,
|
||||
|
||||
/**
|
||||
* Macro used to generate the paths to the musics.
|
||||
* It should be used in #MACRO_FOR_ALL_MUSICS(M) as a parameter.
|
||||
* Each entry of #MACRO_FOR_ALL_MUSICS(M) will be converted as rsrc/music/name.mp3.
|
||||
* \sa #MACRO_TO_SFX_PATH(P)
|
||||
*/
|
||||
#define MACRO_TO_MUSIC_PATH(P) "rsrc/music/"#P".mp3",
|
||||
|
||||
/**
|
||||
* Macro used to generate the paths to the SFX.
|
||||
* It works the same way as #MACRO_TO_MUSIC_PATH(P),
|
||||
* except the entries are converted to rsrc/sfx/name.wav.
|
||||
* \sa MACRO_TO_MUSIC_PATH(P)
|
||||
*/
|
||||
#define MACRO_TO_SFX_PATH(P) "rsrc/sfx/"#P".wav",
|
||||
|
||||
/**
|
||||
* \enum EnumAudios
|
||||
* \brief Lists audios used in the program.
|
||||
*/
|
||||
typedef enum {
|
||||
MACRO_FOR_ALL_MUSICS(MACRO_MUSIC_ENUM_GEN)
|
||||
NB_MUSIC_DEFINED, ///< Index of this entry == number of musics
|
||||
MACRO_FOR_ALL_SFX(MACRO_SFX_ENUM_GEN)
|
||||
NB_AUDIO_DEFINED ///< Index of this entry == number of audios + 1
|
||||
} EnumAudios;
|
||||
|
||||
/**
|
||||
* \struct AudioHandler
|
||||
* \brief A struct to store and handle all audios.
|
||||
*/
|
||||
typedef struct {
|
||||
bool canPlayAudio; ///< true if audio could be initialized, else false. It shouldn't be changed manually.
|
||||
Mix_Music* musics[NB_MUSIC_DEFINED]; ///< Paths to the musics
|
||||
Mix_Chunk* sfx[NB_AUDIO_DEFINED - NB_MUSIC_DEFINED - 1]; ///< Paths to the SFX
|
||||
} AudioHandler;
|
||||
|
||||
/**
|
||||
* \brief Initializes the audio and creates a new AudioHandler.
|
||||
* \param[in] volMusic Volume of the music
|
||||
* \param[in] volSFX Volume of the sound effects.
|
||||
* \return A new AudioHandler.
|
||||
* \warning The program won't know if opening the audio device and the files failed.
|
||||
*/
|
||||
AudioHandler newAudioHandler(int volMusic, int volSFX);
|
||||
|
||||
/**
|
||||
* \brief Changes volume for the SFX.
|
||||
* \param[in] volSFX The new volume for the SFX.
|
||||
*/
|
||||
void changeSFXVol(int volSFX);
|
||||
|
||||
/**
|
||||
* \brief Frees the music and SFX, and un-initializes the audio.
|
||||
* \param[in,out] audioHandler A pointer to the AudioHandler to be freed.
|
||||
*/
|
||||
void freeAudioHandler(AudioHandler audioHandler);
|
||||
|
||||
/**
|
||||
* \brief Plays a music. If another music is currently playing, the previous one will fade out.
|
||||
* \param[in] music The music to be played. Should be a value of #EnumAudios starting with MUSIC_.
|
||||
* \param[in] audioHandler The AudioHandler used to store the musics.
|
||||
*/
|
||||
void playMusic(EnumAudios music, AudioHandler audioHandler);
|
||||
|
||||
/**
|
||||
* \brief Plays a sound effect.
|
||||
* \param[in] sfx The SFX to be played. Should be a value of #EnumAudios starting with SFX_.
|
||||
* \param[in] channel The channel the SFX should be played on.
|
||||
* \param[in] audioHandler The AudioHandler used to store the SFX.
|
||||
*/
|
||||
void playSFX(EnumAudios sfx, int channel, AudioHandler audioHandler);
|
||||
|
||||
|
||||
#endif // AUDIO_H
|
@ -0,0 +1,9 @@
|
||||
#ifndef ARRAY_COORD_INCLUDED
|
||||
#define ARRAY_COORD_INCLUDED
|
||||
|
||||
#include "model/Coord.h"
|
||||
#include "engine/ArrayUtils.h"
|
||||
|
||||
GENERATE_DYNAMIC_ARRAY(Coord)
|
||||
|
||||
#endif //ARRAY_COORD_INCLUDED
|
Binary file not shown.
@ -0,0 +1,7 @@
|
||||
#include "debug/printer.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void printCoord(const Coord coord) {
|
||||
fprintf(stderr, "(%d, %d)", coord.x, coord.y);
|
||||
}
|
||||
|
@ -1,35 +0,0 @@
|
||||
#include "engine/Audio.h"
|
||||
|
||||
struct params {
|
||||
Mix_Music* music; // AA pointer to the Mix_Music struct used for the current track
|
||||
char* path; // The path to the new track to be played
|
||||
};
|
||||
|
||||
int fadeOut(void* args) {
|
||||
struct params* argsCast = (struct params*)args;
|
||||
Mix_Music* newMusic = Mix_LoadMUS(argsCast->path);
|
||||
if (argsCast->music != NULL) {
|
||||
if(Mix_FadeOutMusic(500) == 0) {
|
||||
while (Mix_PlayingMusic()) {
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newMusic != NULL) {
|
||||
argsCast->music = newMusic;
|
||||
return Mix_PlayMusic(argsCast->music,-1);
|
||||
}
|
||||
else return -2;
|
||||
}
|
||||
|
||||
void switchMusic(Mix_Music* music, char path[]) {
|
||||
struct params args = {
|
||||
.music = music,
|
||||
.path = path
|
||||
};
|
||||
SDL_Thread* thread = SDL_CreateThread(&fadeOut, "Fade out", (void*)&args);
|
||||
|
||||
|
||||
SDL_DetachThread(thread); // Won't wait until thread is done to continue
|
||||
}
|
||||
|
@ -0,0 +1,186 @@
|
||||
#include "engine/AudioHandler.h"
|
||||
|
||||
// A channel represents the number of SFX we can play at the same time.
|
||||
// We normally should use only 1 channel, and we add one for safety.
|
||||
#define NBCHANNELS 2
|
||||
|
||||
// Local functions
|
||||
|
||||
/*
|
||||
* Used in a thread. Fades out the currently played music,
|
||||
* then plays the music passed in as parameter.
|
||||
* args should be passed as a pointer to void,
|
||||
* but should actually be a pointer to Mix_Music.
|
||||
* It is the music we want to play next;
|
||||
*/
|
||||
int fadeOut(void* args) {
|
||||
// Since args is a pointer to void
|
||||
// (the way C handles undefined types),
|
||||
// casting args to a pointer to Mix_Music
|
||||
Mix_Music* = (Mix_Music*)args;
|
||||
int ret;
|
||||
|
||||
if(Mix_FadeOutMusic(500) == 0) { // Starting the fadeout
|
||||
while (Mix_PlayingMusic()) {
|
||||
; // Waiting until it's done
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr,"WARNING: couldn't fade out");
|
||||
Mix_HaltMusic();
|
||||
}
|
||||
|
||||
ret = Mix_PlayMusic(argsCast->music,-1);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr,"WARNING: %s\n",Mix_GetError());
|
||||
}
|
||||
|
||||
// We're supposed to return an int to use SDL_thread.
|
||||
// We're not using the return value though.
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Global functions
|
||||
|
||||
AudioHandler newAudioHandler(int volMusic, int volSFX) {
|
||||
AudioHandler audioHandler;
|
||||
int nb_SFX = NB_AUDIO_DEFINED - NB_MUSC_DEFINED - 1;
|
||||
|
||||
// Generating paths to musics and SFX files using macros
|
||||
char* musicsPaths[] = {MACRO_FOR_ALL_MUSICS(MACRO_TO_MUSIC_PATH)};
|
||||
char* sfxPaths[] = {MACRO_FOR_ALL_SFX(MACRO_TO_SFX_PATH)};
|
||||
audioHandler.canPlayAudio = true;
|
||||
|
||||
// Initialize + verify opening audio device
|
||||
if (0 != Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024)) {
|
||||
fprintf(stderr,"Error when initializing audio.\n");
|
||||
return audioHandler;
|
||||
}
|
||||
|
||||
audioHandler.canPlayAudio = true;
|
||||
|
||||
fprintf(stderr,"Musics: %d\nSFX: %d\n",NB_MUSIC_DEFINED,nb_SFX);
|
||||
|
||||
// Loading musics
|
||||
for (size_t i = 0; i < NB_MUSIC_DEFINED; i++) {
|
||||
if ((audioHandler.musics[i] = LoadMUS(musicPaths[i]) == NULL)) {
|
||||
fprintf(stderr,"WARNING: %s\n",Mix_GetError());
|
||||
} else {
|
||||
fprintf(stderr,"Loaded %s\n",musicPaths[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Mix_VolumeMusic(volMusic);
|
||||
|
||||
// Initializing SFX
|
||||
Mix_AllocateChannels(NBCHANNELS);
|
||||
|
||||
// Loading SFX
|
||||
for (size_t i = 0; i < nb_SFX; i++) {
|
||||
if ((audioHandler.sfx[i] = Mix_LoadWAV(sfxPaths[i])) == NULL) {
|
||||
fprintf(stderr,"WARNING: %s\n",Mix_GetError());
|
||||
} else {
|
||||
fprintf(stderr,"Loaded %s\n",sfxPaths[i]);
|
||||
}
|
||||
}
|
||||
|
||||
changeSFXVol(volSFX);
|
||||
return audioHandler;
|
||||
}
|
||||
|
||||
void changeSFXVol(int volSFX) {
|
||||
for (size_t i = 0; i < NBCHANNELS; i++) {
|
||||
Mix_Volume(i, volSFX);
|
||||
}
|
||||
}
|
||||
|
||||
void freeAudioHandler(AudioHandler* audioHandler) {
|
||||
// Freeing the music
|
||||
Mix_HaltMusic();
|
||||
for (size_t i = 0; i < NB_MUSIC_DEFINED; i++) {
|
||||
if (audioHandler->musics[i] != NULL) {
|
||||
Mix_FreeMusic(audioHandler->musics[i]);
|
||||
audioHandler->musics[i] = NULL; // "to be safe...", says the docs
|
||||
}
|
||||
}
|
||||
|
||||
// Freeing the SFX
|
||||
Mix_HaltChannel(-1); // Stopping playback on ALL channels
|
||||
for (size_t i = 0; i < NB_AUDIO_DEFINED - NB_MUSIC_DEFINED - 1; i++) {
|
||||
if (audioHandler->sfx[i] != NULL) {
|
||||
Mix_FreeChunk(audioHandler->sfx[i]);
|
||||
audioHandler->sfx[i] = NULL; // "to be safe...", says the docs
|
||||
}
|
||||
}
|
||||
Mix_AllocateChannels(0); // Unallocating all channels
|
||||
|
||||
// Un-initializing the audio
|
||||
Mix_CloseAudio();
|
||||
|
||||
audioHandler->canPlayAudio = false;
|
||||
}
|
||||
|
||||
void playMusic(EnumAudios music, AudioHandler audioHandler) {
|
||||
// music should be a value of EnumAudios that starts by MUSIC_
|
||||
// In other words, it should be an enumerator before NB_MUSIC_DEFINED.
|
||||
if (music >= NB_MUSIC_DEFINED) {
|
||||
fprintf(stderr,"WARNING: tried to play an arbitrary value as a music\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Checking if audio has been opened.
|
||||
if (!(audioHandler.canPlayAudio)) {
|
||||
fprintf(stderr,"WARNING: tried to play a music with an unusable AudioHandler\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// If another music is playing, fading the previous one out
|
||||
if (Mix_PlayingMusic()) {
|
||||
// Creating the thread, passing the music as parameter
|
||||
SDL_Thread* thread = SDL_CreateThread(&fadeOut, "Fade out", (void*)audioHandler.musics[music]);
|
||||
if (thread == NULL) {
|
||||
fprintf(stderr,"WARNING: couldn't create thread to fade out music\n");
|
||||
}
|
||||
// playMusic won't wait until the thread is finished to end
|
||||
SDL_DetachThread(thread);
|
||||
// No other music is playing: starting a music normally.
|
||||
} else {
|
||||
if (Mix_PlayMusic(audioHandler.musics[music],-1) != 0) {
|
||||
fprintf("WARNING: %s\n",Mix_GetError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void playSFX(EnumAudios sfx, AudioHandler audioHandler) {
|
||||
int nb_SFX = NB_AUDIO_DEFINED - NB_MUSIC_DEFINED - 1;
|
||||
int channel;
|
||||
Mix_Chunk* chunkSFX;
|
||||
|
||||
// sfx should be a value of EnumAudios that starts by SFX_.
|
||||
// In other words, it should be an enumerator after NB_MUSIC_DEFINED but before NB_AUDIO_DEFINED.
|
||||
if (sfx <= NB_MUSIC_DEFINED || sfx >= NB_AUDIO_DEFINED) {
|
||||
fprintf(stderr,"WARNING: tried to play an arbitrary value as a SFX\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Checking if SFX has been opened.
|
||||
if (!(audioHandler.canPlayAudio)) {
|
||||
fprintf(stderr,"WARNING: tried to play an SFX with an unusable AudioHandler\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Getting actual chunk
|
||||
chunkSFX = audioHandler.sfx[sfx - NB_MUSIC_DEFINED - 1];
|
||||
// Getting first available channel
|
||||
channel = Mix_GroupAvailable(-1);
|
||||
if (channel == -1) {
|
||||
fprintf(stderr,"WARNING: cannot play SFX because all channels are used\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Playing the SFX once (0 repetitions)
|
||||
if (Mix_PlayChannel(channel,chunkSFX,0) != 0) {
|
||||
fprintf(stderr,"WARNING: %s\n",Mix_GetError());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in new issue