Obligement - L'Amiga au maximum

Jeudi 23 novembre 2017 - 10:04  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Hit Parade
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

 · Actualité (récente)
 · Actualité (archive)
 · Comparatifs
 · Dossiers
 · Entrevues
 · Matériel (tests)
 · Matériel (bidouilles)
 · Points de vue
 · En pratique
 · Programmation
 · Reportages
 · Tests de jeux
 · Tests de logiciels
 · Tests de compilations
 · Articles divers

 · Articles in english
 · Articles in other languages


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Moteurs de recherche
 · Pages de liens
 · Constructeurs matériels
 · Matériel
 · Autres sites de matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Développeurs logiciels
 · Logiciels
 · Développeurs de jeux
 · Jeux
 · Autres sites de jeux
 · Scène démo
 · Divers
 · Informatique générale


Jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Contact

David Brunet

Courriel

 


Programmation : MUI - gestion CDDA
(Article écrit par Mathias Parnaudeau - août 2004)


Chapitre 6

Après être entré dans les profondeurs de MUI, on s'en écarte un peu pour une partie cruciale de notre lecteur, à savoir le module de gestion des CD. Toujours dans un soucis d'apprendre à bien programmer (en MUI ou pas), on exclura toute fonction d'accès aux disques de notre source lié à l'interface. Cette modularité est indispensable pour que les sources restent lisibles et cohérents. Ainsi, même si on utilise plusieurs API différentes (suivant l'OS par exemple) pour accéder aux disques, l'interface graphique n'a pas à s'en soucier.

Module CDDA

Nous envisageons donc un module que nous appellerons "cdmanager". C'est à lui que notre interface va s'adresser pour lui demander si un disque est présent, combien de titres se trouvent dessus, etc. Nous n'implémenterons que les fonctionnalités de base car bien qu'indispensable, ce module ne doit pas nous détourner de notre centre d'intérêt du moment : MUI.

Pour la lecture de CD, la cdda.library vient simplifier nos affaires sur MorphOS. Sur AmigaOS, il existe la cdplay.library qui est moins évoluée mais doit offrir des fonctionnalités de base équivalentes. Sinon les lecteurs avaient l'habitude de piloter directement le lecteur avec des commandes SCSI envoyées au device approprié. Pour rester simple, sous AmigaOS, nous ne pourrons que simuler la présence d'un CD en renvoyant des valeurs fixes. C'est dommage mais dans le fond ça ne gênera pas au niveau de l'interface. Libre à vous d'implémenter un réel gestionnaire de CD avec des accès bas niveau !

De quelles fonctions avons-nous besoin ?

Nous devons pouvoir :
  • Initialiser et libérer le module.
  • Réserver le lecteur pour notre usage personnel et annuler ce verrou.
  • Récupérer le nombre de pistes du CD courant.
  • Jouer une piste en particulier et arrêter la lecture.
Malheureusement, d'autres fonctionnalités ne seront pas gérées : pause, éjection, etc. il est toujours envisageable de les implémenter par la suite. L'important pour nous tient dans la réalisation d'un module indépendant auquel on peut accéder simplement depuis notre interface. De même, la gestion interne du module ne présente peut-être pas toutes les garanties nécessaires, bien qu'il soit fonctionnel.

Voici les prototypes des fonctions correspondants à celles énumérées ci-dessus :

int CDM_Initialize(STRPTR device, int unit);
void CDM_DeInitialize(void);
int CDM_GetNumTracks(void);
void CDM_PlayTrack(int num);
void CDM_StopTrack(void);

Le préfixe CDM signifie "CD Manager", soit le nom de notre module. Les noms des fonctions sont en anglais, il s'agit d'une convention personnelle. Seules deux fonctions prennent des arguments : l'initialisation qui indique le device et l'unité choisis, la lecture d'une piste donnée (de 1 à GetNumTracks()).

Entre boutons et actions

Les détails du module CDM restent à votre entière disposition dans le source, nous ne nous attardons pas dessus. En revanche, voyons l'intégration du module dans l'interface.

L'initialisation de CDM a lieu dès le démarrage de l'application. Le device et son unité sont passés en dur, ce qui a pour conséquence de rendre actuellement inutile l'onglet de configuration. Mais pas de panique, tout est en place pour remédier à ça. D'autres part, les deux valeurs sont présentées dans une structure appelée Config que l'on renseigne donc manuellement pour l'instant mais là encore, il suffit d'imaginer une fonction de lecture d'un fichier de configuration au démarrage pour rendre tout cela beaucoup plus attrayant. Prévoyez aussi la sauvegarde. ;-)

Les fonctions de verrouillage (lock) sont utilisées uniquement en interne. Le module doit s'assurer qu'il a un accès privilégié au lecteur et au disque pour y lire des informations. Une des premières données que l'on va lire pour en rendre compte à l'utilisateur concerne le nombre de pistes du CD présent. La valeur obtenue permet d'enrichir la liste avec autant de nouvelles entrées qu'il n'y a de pistes sur le CD, grâce à la méthode MUIM_List_InsertSingle de l'objet liste. Il semble que ce dernier ne copie pas dans ses données personnelles les chaînes insérées, c'est pourquoi nous avons déclaré un tableau (de 24 lignes maximum) pour y conserver les chaînes sur lesquelles pointe l'objet liste.

Voilà pour les opérations qui ont lieu une seule fois. Maintenant, le temps est venu de gérer l'interaction des boutons et de la liste avec le module CDM. Rappelons les actions de base :
  • La lecture d'une piste est commandée quand on clique sur une des entrées de la liste, sur les boutons "Jouer", "Précédent" et "Suivant". Ceci implique la récupération du numéro de piste à jouer, la mise en action du composant Busy et l'appel à la lecture effective de la piste.
  • L'arrêt de la lecture, provoquée par un clic sur "Stopper" ou par la fermeture de l'application (dans ce cas, c'est l'appel à la fonction de libération des ressources qui s'en charge).
Interactions en pratique

Si les actions sur le module CDM étaient gérées par un composant MUI, on pourrait uniquement (et simplement !) utiliser le mécanisme de notification. En attendant, nous devons ajouter deux hooks :
  • Un pour la lecture, quel que soit le composant qui est à l'origine de cette action : il récupère la piste courante de la liste, commande sa lecture à CDM et active le composant Busy.
  • L'autre pour l'arrêt.
Quelques changements concernant le composant Busy ont été apportés. Il n'est plus lié aux boutons Jouer et Stopper mais à l'action correspondante, ce qui est plus intéressant ! En effet, le contrôle centralisé en un seul endroit, dans le hook. La démarche est la suivante : un clic sur le bouton Jouer définit l'entrée active de la liste... action sur laquelle repose le hook ! Voilà un joli travail d'équipe.

Pour un degré de finition plus avancé, il faudrait récupérer l'événement comme quoi le morceau est fini et passer à la piste suivante si on n'est pas déjà sur la dernière. Le changement de l'entrée active de la liste entraînerait sa lecture (grâce au hook_PlayTrack).

Voici le source :

/*
 * DisKo6.c (04/08/04)
 *
 */

#include <stdio.h>
#include <string.h>
#include <libraries/mui.h>
#include <proto/muimaster.h>
#include <proto/intuition.h>
#include <proto/exec.h>
#include <clib/alib_protos.h>

#include <mui/Busy_mcc.h>

#include <SDI_hook.h>

#include "cdmanager.h"

#define MAKE_ID(a,b,c,d)  ((ULONG) (a)<<24 | (ULONG) (b)<<16 | (ULONG) (c)<<8 | (ULONG) (d))

struct Library *MUIMasterBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;

#ifdef __amigaos4__
struct IntuitionIFace *IIntuition;
struct MUIMasterIFace *IMUIMaster;
#endif

#define	MUIA_Application_UsedClasses	0x8042e9a7	/* V20 STRPTR *	i..	*/

static char *ClassList[] =
{
	"Busy.mcc",
	NULL
};

static Object *app = NULL;
static Object *window = NULL;
static Object *list;
static Object *busy;

static char *Pages[] = { "Utilisation", "Configuration", NULL };

struct Config {
	int unit;
	char device[32];
} config;

static char titles[24][64];


/*
 * Hooks nécessaires en attendant mieux ... ;-)
 */

HOOKPROTONH(ChangeVolume, ULONG, APTR obj, struct TagItem *tag_list)
{
	int val;

	get(obj, MUIA_Numeric_Value, &val);

	printf("Nouvelle valeur : %d\n", val);

	return TRUE;
}
MakeStaticHook(hook_ChangeVolume, ChangeVolume);

HOOKPROTONH(hook_PlayTrackFunc, ULONG, APTR obj, struct TagItem *tag_list)
{
	int val;

	get(obj, MUIA_List_Active, &val);
	val++;

	CDM_PlayTrack(val);

	set(busy, MUIA_Busy_Speed, 20);

	return TRUE;
}

MakeStaticHook(hook_PlayTrack, hook_PlayTrackFunc);

HOOKPROTONH(hook_StopTrackFunc, ULONG, APTR obj, struct TagItem *tag_list)
{
	CDM_StopTrack();

	set(busy, MUIA_Busy_Speed, MUIV_Busy_Speed_Off);

	return TRUE;
}

MakeStaticHook(hook_StopTrack, hook_StopTrackFunc);

/*
 * Construction et ouverture de la fenêtre principale.
 * Cette fonction est appelée quand on sait que tout a bien été initialisé.
 */
Object * OpenMainWindow(void)
{
	Object *bt_play, *bt_stop, *bt_previous, *bt_next, *bt_pause, *bt_eject;
	Object *lv_pistes;
	Object *sl_volume;

	Object *sl_unit;
	Object *str_device;
	Object *txt_info;

	int i, tracks;

	/* Description de l'interface et de ses propriétés */	

	app = (Object *)ApplicationObject,
		MUIA_Application_Author, "corto@guru-meditation.net",
		MUIA_Application_Base, "DISKO",
		MUIA_Application_Title, "DisKo - Exemple 6",
		MUIA_Application_Version, "$VER: DisKo 1.06 (27/07/04)",
		MUIA_Application_Copyright, "Mathias PARNAUDEAU",
		MUIA_Application_Description, "Player de CD audio minimaliste",
		MUIA_Application_HelpFile, NULL,
		MUIA_Application_UsedClasses, ClassList,

		SubWindow, window = WindowObject,
			MUIA_Window_Title, "DisKo - release 6",
			MUIA_Window_ID, MAKE_ID('W', 'I', 'N', '1'),
			WindowContents, VGroup,

				Child, RegisterGroup(Pages),
					MUIA_Register_Frame, TRUE,

					// Onglet utilisation
					Child, VGroup,
						Child, HGroup,
							Child, lv_pistes = ListviewObject,
								MUIA_Listview_List, list = ListObject,
									MUIA_Frame, MUIV_Frame_InputList,
									//MUIA_List_Format, "P=\33r",
									MUIA_List_Active, MUIV_List_Active_Top,
									MUIA_List_AutoVisible, TRUE,
								End,
							End,

							Child, VGroup,
								Child, Label("Volume"),
								Child, sl_volume = SliderObject,
									MUIA_Group_Horiz, FALSE,
									MUIA_Numeric_Min, 0,
									MUIA_Numeric_Max, 100,
									MUIA_Numeric_Value, 38,
									MUIA_Numeric_Reverse, TRUE,
								End,
							End,
						End,

						Child, VSpace(2),

						Child, HGroup,
							Child, txt_info = TextObject,
								MUIA_Frame, MUIV_Frame_Text,
								MUIA_Text_Contents, "Information piste",
							End,
							Child, busy = BusyObject,
								MUIA_Busy_Speed, MUIV_Busy_Speed_Off,
							End,
						End,
						Child, HGroup,
							Child, bt_previous = KeyButton("Précédent", 'p'),
							Child, bt_next = KeyButton("Suivant", 'v'),
							Child, bt_play = KeyButton("Jouer", 'j'),
							Child, bt_pause = KeyButton("Pause", 'a'),
							Child, bt_stop = KeyButton("Stopper", 's'),
							Child, bt_eject = KeyButton("Ejecter", 'e'),
						End,

					End,

					// Onglet configuration
					Child, VGroup, GroupFrameT("Lecteur CD"),
						Child, ColGroup(2),
							Child, Label2("Device"),
							Child, str_device = String(config.device, 32),
							Child, Label1("Unit" ),
							Child, sl_unit = SliderObject,
								MUIA_Group_Horiz, TRUE,
								MUIA_Numeric_Min, 0,
								MUIA_Numeric_Max, 7,
								MUIA_Numeric_Value, config.unit,
							End,
						End,
						Child, KeyButton("Valider", 'd'),
					End,
				End,
			End,
		End,
	End;

	if (app){
		/* On fixe quelques valeurs et notifications */
	
		DoMethod(window,
			MUIM_Notify, MUIA_Window_CloseRequest, TRUE,
			app, 2,
			MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);

		DoMethod(busy, MUIM_Busy_Move, FALSE);

		tracks = CDM_GetNumTracks();
		for (i=1 ; i<=tracks ; i++){
			sprintf(titles[i], "Piste %d", i);
			DoMethod(list, MUIM_List_InsertSingle, titles[i], MUIV_List_Insert_Bottom);
		}

		DoMethod(sl_volume, MUIM_Notify, MUIA_Numeric_Value, MUIV_EveryTime,
					sl_volume, 2, MUIM_CallHook, &hook_ChangeVolume);
/*
		DoMethod(sl_volume, MUIM_Notify, MUIA_Pressed, FALSE,
					sl_volume, 2, MUIM_CallHook, &hook_ChangeVolume);
*/
		DoMethod(bt_play, MUIM_Notify, MUIA_Pressed, FALSE,
					list, 2, MUIM_CallHook, &hook_PlayTrack); //MUIM_List_Jump, MUIV_List_Jump_Active);

		DoMethod(bt_stop, MUIM_Notify, MUIA_Pressed, FALSE,
					bt_stop, 2, MUIM_CallHook, &hook_StopTrack);

		set(list, MUIA_List_Active, MUIV_List_Active_Top);

		DoMethod(list, MUIM_Notify, MUIA_List_Active, MUIV_EveryTime,
			list, 2, MUIM_CallHook, &hook_PlayTrack);

		DoMethod(bt_previous, MUIM_Notify, MUIA_Pressed, FALSE,
					list, 3, MUIM_Set, MUIA_List_Active, MUIV_List_Active_Up);

		DoMethod(bt_next, MUIM_Notify, MUIA_Pressed, FALSE,
					list, 3, MUIM_Set, MUIA_List_Active, MUIV_List_Active_Down);

		SetAttrs(window, MUIA_Window_Open, TRUE, TAG_END);
	}

	return app;
}

/*
 * Initialisation et vérification de tout ce qui est nécessaire à la bonne exécution
 * de l'application : ouverture des bibliothèques, test de présence des classes MCC, ...
 */
int Initialize(void)
{
	int res = 1;
	Object *busy = NULL;
	int cdm;

	// Initialisation de la configuration (chargement éventuel à partir d'un fichier)
#if defined(__amigaos4__)
	config.unit = 1;
	strcpy(config.device, "a1ide.device");
#elif defined(__MORPHOS__)
	config.unit = 2;
	strcpy(config.device, "ide.device");
#else
	// Pour UAE, ça fonctionne pour moi avec : uaescsi.device, unit 0
	config.unit = 1;
	strcpy(config.device, "scsi.device");
#endif

	IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 39L);
	MUIMasterBase = OpenLibrary(MUIMASTER_NAME, MUIMASTER_VMIN);
	cdm = CDM_Initialize(config.device, config.unit);

	if (IntuitionBase == NULL){
		printf("Impossible d'ouvrir 'intuition.library' V39\n");
		res = 0;
	}
	if (MUIMasterBase == NULL){
		printf("Impossible d'ouvrir '%s' V%d\n", MUIMASTER_NAME, MUIMASTER_VMIN);
		res = 0;
	}

#ifdef __amigaos4__
	IIntuition = (struct IntuitionIFace *)GetInterface((struct Library *)IntuitionBase, "main", 1, NULL);
	if (!IIntuition){
		printf("Impossible d'obtenir l'interface IIntuition\n");
		res = 0;
	}
	IMUIMaster = (struct MUIMasterIFace *)GetInterface(MUIMasterBase, "main", 1, NULL);
	if (!IMUIMaster){
		printf("Impossible d'obtenir l'interface IMUIMaster\n");
		res = 0;
	}
#endif

	if (cdm){
		printf("Impossible d'initialiser le module CDDA avec %s, unité %d\n", config.device, config.unit);
		printf("Veuillez modifier le device et l'unité dans la fonction Initialize() de DisKo6.c\n");
		printf("Et vérifiez qu'un CD audio est bien présent dans le lecteur ! ;-)\n");
		res = 0;
	}

	busy = BusyObject, End;
	if (busy == NULL){
		printf("Classe Busy manquante\n");
		res = 0;
	}

	MUI_DisposeObject(busy);

	return res;
}


/*
 * Fermeture et libération de tout ce qui a été initialisé au démarrage.
 */
void DeInitialize(void)
{

	CloseLibrary(MUIMasterBase);
	CloseLibrary((struct Library *)IntuitionBase);
	CDM_DeInitialize();

#ifdef __amigaos4__
	if (IMUIMaster) {
		DropInterface((struct Interface *)IMUIMaster);
	}
	if (IIntuition) {
		DropInterface((struct Interface *)IIntuition);
	}
#endif
}


/*
 * Programme principal : il appelle les initialisations, ouvre la fenêtre puis gère
 * les événements jusqu'à ce qu'on ferme l'application, condition de libération
 * des ressources
 */
int main(int argc, char **argv)
{
	int res = 0;

	if (Initialize()){
		app = OpenMainWindow();
		if (app){
			/* Boucle de gestion des événements, toujours la même */
			ULONG sigs = 0;

			while (DoMethod(app,MUIM_Application_NewInput,&sigs) != MUIV_Application_ReturnID_Quit)
			{
				if (sigs)
				{
					sigs = Wait(sigs | SIGBREAKF_CTRL_C);
					if (sigs & SIGBREAKF_CTRL_C) break;
				}
			}

			/* Libération des ressources et fermeture */
			set(window, MUIA_Window_Open, FALSE);
			MUI_DisposeObject(app);
		}else{
			res = 2;
		}
	}else{
		res = 1;
	}
	DeInitialize();

	return res;
}

Notions acquises dans ce chapitre
  • Avantages, conception et écriture d'un module indépendant à la GUI.
  • Amélioration de l'interaction entre les composants.
  • Révision des hooks, notifications et appels de méthodes


[Retour en haut] / [Retour aux articles] [Article précédent] / [Article suivant]