Files

341 lines
15 KiB
C

// **********************************************************************************************************************************************************************************
// Fichier AUDIO.C
//
// Contient toutes les fonctions liées à l'audio.
// **********************************************************************************************************************************************************************************
#define __DEBUG_AUDIO //Décommenter cette ligne pour avoir les messages de debug concernant l'audio
// Include :
#include "main.h"
// Prototypes :
void gestion_audio(void);
int8_t init_audio(void);
int8_t emission_bip(char * nom_fichier, uint16_t nb_repetitions, uint16_t periode_boucles, uint16_t nb_boucles);
int8_t lecture_mp3(char * nom_fichier, uint16_t nb_repetitions);
int8_t mise_a_jour_etat_jack();
int8_t set_volume(float volume, bool echo);
int8_t alimentation_ampli_audio(void);
// Variables globales :
struct Struct_audio audio;
//**********************************************************************************************************************************************************************************
// Fonction : Gestion de l'audio en tâche de fond
// Reçoit : Rien
// Retourne : Rien
//**********************************************************************************************************************************************************************************
void gestion_audio(void)
{
char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG];
switch(audio.sequenceur.etat)
{
//--------------------------------------------------------
case ETAT_SEQUENCEUR_AUDIO_INIT :
{
if (audio.sequenceur.compteur == 0)
{
audio.sequenceur.etat = ETAT_SEQUENCEUR_AUDIO_REPOS;
}
break;
}
//--------------------------------------------------------
case ETAT_SEQUENCEUR_AUDIO_REPOS :
{
if (audio.sequenceur.compteur == 0)
{
audio.sequenceur.compteur = AUDIO_PERIODE_SURVEILLANCE;
mise_a_jour_etat_jack();
}
break;
}
//--------------------------------------------------------
case ETAT_SEQUENCEUR_AUDIO_DEBUT_BOUCLE :
{
mise_a_jour_etat_jack(); // Pour que la mise à jour se fasse quand même de temps en temps...
lecture_mp3(audio.boucle.nom_fichier, audio.boucle.nb_repetitions);
audio.sequenceur.compteur = audio.boucle.periode_boucles;
audio.sequenceur.etat = ETAT_SEQUENCEUR_AUDIO_TEMPO_BOUCLE;
break;
}
//--------------------------------------------------------
case ETAT_SEQUENCEUR_AUDIO_TEMPO_BOUCLE :
{
if (audio.sequenceur.compteur == 0)
{
if (audio.boucle.compteur_boucles > 0)
{
audio.boucle.compteur_boucles --;
audio.sequenceur.etat = ETAT_SEQUENCEUR_AUDIO_DEBUT_BOUCLE;
}
else
{
audio.boucle.nom_fichier[0] = 0;
audio.boucle.nb_repetitions = 0;
audio.boucle.periode_boucles = 0;
audio.boucle.nb_boucles = 0;
audio.sequenceur.etat = ETAT_SEQUENCEUR_AUDIO_REPOS;
}
}
break;
}
//--------------------------------------------------------
default :
{
sprintf(tmp, "L'état du séquenceur Audio est inconnu [%i]. Il est repositionné au REPOS", audio.sequenceur.etat);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR);
audio.sequenceur.etat = ETAT_SEQUENCEUR_AUDIO_REPOS;
break;
}
}
}
//==================================================================================================================================================================
// Fonction : Initialise les fonctions audio
// Reçoit : Rien
// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée
//==================================================================================================================================================================
int8_t init_audio(void)
{
// Ajustement des variables selon la génération de ShipBase en cours :
if (systeme.generation_shipbase == GENERATION_SHIPBASE_V1)
{
audio.sortie_gpio_cm_do_audio_sd = CM3P_DO_AUDIO_SD;
audio.entree_gpio_cm_di_jack_detect = 0; // Non disponible sur V1
}
else
{
audio.sortie_gpio_cm_do_audio_sd = CM4_DO_AUDIO_SD;
audio.entree_gpio_cm_di_jack_detect = CM4_DI_JACK_DETECT;
}
// Mise à jour du niveau de son (sans émission de bip) :
// if (set_volume(audio.consigne_volume, AUDIO_SET_VOLUME_ECHO_OFF) == -1) return(-1); // La valeur a été récupérée dans la BDD par 'init_bdd()'
set_volume(audio.consigne_volume, AUDIO_SET_VOLUME_ECHO_OFF); // Mise à jour du volume au niveau matériel. NB : l'alimentation de l'ampli est faite en fin de 'set_volume()' et tient compte de l'état de 'Fonction_jack'
// Lancement musique d'accueil :
if (systeme.generation_shipbase == GENERATION_SHIPBASE_V1) lecture_mp3("netflix.mp3", 0);
else lecture_mp3("battement_coeur.mp3", 0);
// Affichage du message de débug :
affichage_debug(__FILE__, __LINE__, "Initialisation audio\t\t: OK", TYPE_MESSAGE_DEBUG_OK);
return(0);
}
//==================================================================================================================================================================
// Fonction : Demande l'émission d'un bip qui peut être complexe (un ou plusieurs bips, répétés un certain nombre de fois à un intervalle défini). A noter que cette
// fonction permet aussi d'arrêter la séquence par anticipation si besoin.
// Reçoit : Le nom du fichier MP3 à utiliser comme son de base (ou "Off" pour stopper), le nombre de répétitions du son de base (joués à la suite les uns des autres),
// la tempo d'attente entre chaque boucles et le nombre de boucles.
// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée
//==================================================================================================================================================================
int8_t emission_bip(char * nom_fichier, uint16_t nb_repetitions, uint16_t periode_boucles, uint16_t nb_boucles)
{
FILE *file;
char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG];
// Détection d'une commande d'arrêt de lecture :
// Nb : le nom spécifique "Off" est traité de façon insensible à la casse (donc "OFF", "Off", "off" et tous les autres intermédiaires fonctionnent)
if ( (strlen(nom_fichier) == 3)
&& (tolower(nom_fichier[0]) == 'o')
&& (tolower(nom_fichier[1]) == 'f')
&& (tolower(nom_fichier[2]) == 'f') )
{
audio.boucle.nom_fichier[0] = 0;
audio.boucle.nb_repetitions = 0;
audio.boucle.periode_boucles = 0;
audio.boucle.nb_boucles = 0;
audio.boucle.compteur_boucles = 0;
audio.sequenceur.etat = ETAT_SEQUENCEUR_AUDIO_REPOS;
return (0);
}
// Test du fichier :
sprintf(tmp, "%s/%s", REPERTOIRE_CONFIG_SONS, nom_fichier); // Chemin complet du fichier dont le nom a été reçu
file = fopen(tmp, "r"); // Tentative d'ouverture en lecture du fichier
if (file == NULL)
{
sprintf(tmp, "Le fichier audio à lire [%s] n'existe pas dans le répertoire SONS [%s]. La lecture n'est pas lancée...", nom_fichier, REPERTOIRE_CONFIG_SONS);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR);
return(-1); // Si erreur d'ouverture, sortie en erreur (et, surtout, pas de lancementd e lecture)
}
else fclose(file); // Si pas d'erreur, on referme immédiatement le fichier avantd e continuer...
// Cas où le fichier existe bien :
strcpy(audio.boucle.nom_fichier, nom_fichier);
audio.boucle.nb_repetitions = nb_repetitions;
audio.boucle.periode_boucles = periode_boucles;
audio.boucle.nb_boucles = nb_boucles;
audio.boucle.compteur_boucles = nb_boucles;
audio.sequenceur.etat = ETAT_SEQUENCEUR_AUDIO_DEBUT_BOUCLE;
return(0);
}
//==================================================================================================================================================================
// Fonction : Lance le son correspondant au fichier MP3 passé en paramètre.
// NB : la fonction lance un appel système non bloquant. Le fichier doit être dans REPERTOIRE_CONFIG_SONS.
// Reçoit : Nom complet du fichier (depuis REPERTOIRE_CONFIG_SONS)
// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée
//==================================================================================================================================================================
int8_t lecture_mp3(char * nom_fichier, uint16_t nb_repetitions)
{
char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG];
char loop[15];
if (nb_repetitions > 0) sprintf(loop, "--loop %i", nb_repetitions);
else loop[0] = 0;
sprintf(tmp, "sudo mpg123 %s %s/'%s' > /dev/null 2>&1 &", loop , REPERTOIRE_CONFIG_SONS, nom_fichier); // Attention : le '&' en fin de commande est nécessaire pour que l'appli n'attende pas la fin de la commande, donc de la lecture du morceau...!
system(tmp);
//printf("\tLancement lecture du fichier MP3 via la commande : [%s]\n", tmp);
return(0);
}
//==================================================================================================================================================================
// Fonction : Effectue une lecture matérielle pour définir si une prise jack est connectée ou pas sur la sortie audio de ShipBase
// NB : valable uniquement sur V2.
// NB 2 : cd SDIR #232
// Reçoit : Rien
// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée
//==================================================================================================================================================================
int8_t mise_a_jour_etat_jack()
{
bool flag;
char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG];
if (audio.entree_gpio_cm_di_jack_detect != 0) // Les V1 ne sont pas concernées...
{
// flag = (bool) lecture_entree(TYPE_ENTREES_GPIO, 0, audio.entree_gpio_cm_di_jack_detect, FALSE); // 0 = adresse ShipBase
flag = TRUE; // SDIR #232
if (flag != audio.etat_presence_jack)
{
audio.etat_presence_jack = flag;
alimentation_ampli_audio();
#ifdef __DEBUG_AUDIO
sprintf(tmp, "La présence d'une prise jack sur la sortie audio a changé d'état. Elle est maintenant : %s. NB : Fonction jack = %i", audio.etat_presence_jack ? "Présente" : "Absente", audio.fonction_jack);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
}
}
return (0);
}
//==================================================================================================================================================================
// Fonction : Met à jour le niveau de volume du son de la carte. La consigne est celle reçue en paramètre et la fonction met à jour les paramètres de AlsaMixer.
// Dans le cas où la consigne est à 0, la sortie de commande de l'ampli est carrément coupée.
// Reçoit : Le niveau de volume attendu (de 0 à 100%), en centièmes de pourcent.
// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée
//==================================================================================================================================================================
int8_t set_volume(float volume, bool echo)
{
long min, max;
snd_mixer_t *handle;
snd_mixer_selem_id_t *sid;
const char *card = "default";
const char *selem_name = "PCM";
char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG];
// Vérification de la nouvelle valeur de consigne :
if ((volume < AUDIO_CONSIGNE_MIN) || (volume > AUDIO_CONSIGNE_MAX))
{
sprintf(tmp, "La nouvelle consigne de volume audio n'est pas dans la plage autorisée : %.2f pour [%i ; %i%%]. La commande est ignorée...", volume, AUDIO_CONSIGNE_MIN, AUDIO_CONSIGNE_MAX);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR);
return(-1);
}
// Mise à jour de la nouvelle valeur :
audio.consigne_volume = volume;
// Envoi vers le driver :
snd_mixer_open(&handle, 0);
snd_mixer_attach(handle, card);
snd_mixer_selem_register(handle, NULL, NULL);
snd_mixer_load(handle);
snd_mixer_selem_id_alloca(&sid);
snd_mixer_selem_id_set_index(sid, 0);
snd_mixer_selem_id_set_name(sid, selem_name);
snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
snd_mixer_selem_set_playback_volume_all(elem, volume * max / 100);
snd_mixer_close(handle);
// Activation ampli audio :
alimentation_ampli_audio();
// Enregistrement dans l'historique :
enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_VOLUME_CHANGEMENT_CONSIGNE, 0, audio.consigne_volume, NULL);
#ifdef __DEBUG_AUDIO
sprintf(tmp, "Nouvelle consigne de volume = %.2f %%", audio.consigne_volume);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
// Bip de mise à jour s'il est demandé :
// if (echo) lecture_mp3("button-8.mp3", 0);
if (echo) lecture_mp3("beep-07.mp3", 0);
return(0);
}
//==================================================================================================================================================================
// Fonction : Active ou désactive l'alimentation de l'ampli audio (alimentant le HP intégré) selon la présence ou non d'une prise jack sur la sortie AUDIO
// et selon le paramètre de configuration 'Fonction_jack'
// Reçoit : Rien
// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée
//==================================================================================================================================================================
int8_t alimentation_ampli_audio(void)
{
#ifdef __DEBUG_AUDIO
char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG];
#endif
if ( ((audio.etat_presence_jack == TRUE) && (audio.fonction_jack == AUDIO_FONCTION_JACK_HP_OFF)) || // Si une fiche jack est présente sur la sortie Audio ET que le mode de fonctionnement est de couper le HP dans ce cas...
(audio.consigne_volume == 0) ) // ... OU le volume est à 0
{
// Coupure de l'alimentation de la sortie audio :
ecriture_sortie(TYPE_SORTIE_GPIO, 0, audio.sortie_gpio_cm_do_audio_sd, OFF); // 0 est l'adresse de ShipBase
#ifdef __DEBUG_AUDIO
sprintf(tmp, "L'alimentation de l'ampli audio est passée à OFF car Présence Jack = %s et Fonction Jack = %i", (audio.etat_presence_jack ? "Présente" : "Absente"), audio.fonction_jack);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
}
else // Dans tous les autres cas, l'ampli est alimenté.
{
// Alimentation de la sortie audio :
ecriture_sortie(TYPE_SORTIE_GPIO, 0, audio.sortie_gpio_cm_do_audio_sd, ON); // 0 est l'adresse de ShipBase
#ifdef __DEBUG_AUDIO
sprintf(tmp, "L'alimentation de l'ampli audio est passée à ON car Présence Jack = %s et Fonction Jack = %i", (audio.etat_presence_jack ? "Présente" : "Absente"), audio.fonction_jack);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
}
return(0);
}