|
|||||||||||||||||||||||||||||||||||||||||||
|
Ayant achevé notre série d'articles sur l'animation, nous allons entamer à partir de ce numéro un nouveau thème : la génération de sons via le système, en langage C. Sans vouloir reprendre l'excellent article sur Paula, un bref rappel de la configuration audio matérielle de l'Amiga n'est sans doute pas superflu. Paula (le circuit chargé, entre autres choses, de la reproduction sonore) est composée de quatre canaux audio indépendants, regroupés en deux voix stéréo : les canaux 0 et 3 sont connectés à la sortie gauche, alors que les canaux 1 et 2 sont reliés à la sortie droite. Ces sorties, je vous le rappelle, sont les deux prises Cinch situées à l'arrière de la machine. Chaque canal possède un certain nombre de registres de contrôle permettant de piloter la sortie audio :
Mais cette méthode ne peut s'appliquer si facilement dans un environnement multitâche, et la production de son, comme celle de graphisme, doit s'effectuer en passant par les bibliothèques système de l'Amiga. Le logiciel Il existe donc une plate-forme logicielle pour la gestion des capacités sonores de notre chère machine. Toutefois, compte tenu du type de services attendus d'une telle couche logicielle, celle-ci est beaucoup plus restreinte que, par exemple, la graphics.library. Communication via l'audio.device Comme pour les autres périphériques logiques de l'Amiga, l'audio.device utilise la notion de "IORequest Block" pour la communication avec votre tâche. Cette requête est appelée IOAudio. Pour communiquer avec ce péripéhrique logique, la première chose à faire consiste donc à l'ouvrir. On utilisera pour ce faire la fonction OpenDevice(), à laquelle on passera comme argument une structure IOAudio initialisée à zéro. Cette structure doit être déclarée sous forme de pointeur et allouée dynamiquement dans la mémoire Chip. Les deux fonctions ci-dessous vous permettront respectivement d'allouer et de libérer cette structure. ![]() ![]() Cette plate-forme a pour but d'adapter l'utilisation du composant audio de l'Amiga à son système multitâche, en déchargeant le programmeur d'une série de contrôles systématiques sur l'environnement d'exécution. Voici tout ce que l'audio.device permet de faire :
Maintenant que le périphérique logique audio est ouvert, nous allons étudier comment s'allouer un canal audio en vue de son utilisation dans un environnement multitâche. Le système audio permet de réclamer des canaux audio pour sa propre utilisation, suivant deux méthodes différentes : soit lors de l'appel à la fonction OpenDevice(), soit lors d'un appel séparé à la fonction ADCMD_ALLOCATE. Voyons ensemble la première méthode. On peut établir la priorité avec laquelle une tâche va pouvoir utiliser les canaux audio. Cette priorité s'échelonne entre -128 (minimum) et +127 (maximum). Par défaut, elle est fixée à 0. Pour la modifier, il suffit d'assigner au champ ioa_Request.io_Message.mn_Node.ln_Pri de l'IOAudio Request Block la valeur désirée avant l'appel à OpenDevice(). Maintenant, il s'agit de réclamer les canaux pour la tâche. La première chose à faire, c'est d'indiquer le nombre de canaux que l'on désire utiliser : on met ce nombre dans le champ ioa_Length. Ensuite, on associe au champ ioa_Data un tableau de BYTEs de la taille de ioa_Length. Chaque BYTE contient sous forme d'un code le numéro du canal à réserver (on l'appelle aussi Allocation Byte). Chaque Allocation Byte contient en fait quatre bits (ceux de plus faible poids), un par canal possible. Un bit positionné à 1 correspond directement au canal sélectionné. Pour réserver une paire de canaux pour un usage stéréophonique, il faut positionner deux des bits représentant respectivement les canaux gauche et droite désirés. On ne peut allouer que deux canaux en mode stéréophonique. Toutefois, si l'on indique plusieurs Allocation Byte (et par là même, plusieurs possibilités stéréo), le système choisira une paire quelconque, en fonction de la disponibilité des canaux. Si l'on ne propose qu'une seule paire et que l'un des canaux, ou les deux, sont indisponibles, l'allocation ne s'effectue pas. Pour savoir les résultats d'une demande d'allocation, il faut inspecter le champ ioa_Request.io_Unit. Il est à noter que tout canal alloué par une tâche doit être explicitement libéré lorsque cette tâche n'en a plus besoin, cette libération des ressources n'étant pas automatique. Le tableau ci-dessous reprend l'ensemble des possibilités offertes pour l'allocation des canaux. ![]() ![]() Dans le cas où l'on voudrait modifier la priorité associée à un canal après l'appel à OpenDevice(), il vous faudra utiliser la commande ADCMD_SETPREC. La fonction suivante permet de réaliser ce changement de priorité. ![]() ![]() ![]() Dans toutes les formes d'allocation, votre tâche est mise en sommeil (via la fonction Wait() d'Exec) jusqu'à complétion de la requête système. Chaque fois qu'un ou plusieurs canaux sont libérés, le périphérique logique audio examine les différentes requêtes, en commençant par les plus prioritaires. S'il n'y a pas assez de canaux libres pour répondre à la requête, la tâche restera en sommeil jusqu'à ce que sa requête puisse être honorée. Cette attente est bien mise en évidence dans la routine allocanaux() ou changepriorite(). Par contre, dans toutes les routines utilisant OpenDevice(), il n'y a rien de visible. La fonction OpenDevice() ne retourne rien tant que l'allocation n'a pas été possible. Si vous ne désirez pas que votre tâche attende la fin de l'allocation, vous pouvez positionner dans le champ ioa_Request.io_Flags la valeur ADIOF_NOWAIT : si l'allocation ne peut s'effectuer immédiatement, alors ioa_Request.io_Error sera rempli par le système avec la valeur IERR ALLOCFAILED. Si l'allocation a échoué, la valeur retournée est systématiquement zéro. Utilisation des canaux Quand vous avez ouvert l'audio.device, associé les priorités et alloué les canaux, la phase d'initialisation est terminée et vous pouvez utiliser ces canaux. Lorsque vous avez alloué plusieurs canaux audio, le système remplit le champ ioa_Request.io_Unit avec une valeur représentant les canaux alloués. De même, il assigne une clef d'allocation unique au champ ioa_AllocKey représentant la requête. Si vous créez plusieurs IOAudio Request Block, par exemple à l'aide de la fonction allocaudio(), pour créer une file d'attente de commandes audio, vous devez recopier dans chacun de ces blocs : l'adresse du périphérique logique, le contenu du champ ioa_Request.io_Unit et le contenu du champ ioa_AllocKey, ceci afin de permettre d'envoyer des requêtes aux canaux audio précédemment ouverts. Toutefois, quand vous avez alloué une paire de canaux stéréo, vous n'êtes pas obligé de gérer deux Request Blocks : en effet, il suffit de changer, entre chaque envoi de requête, le contenu du champ ioa_Request.io_Unit avec le numéro du canal droit et gauche. Par exemple, vous vous êtes alloué les canaux 1 et 2. Le numéro de Unit renvoyé par le système est donc 0110. Pour envoyer des commandes séparées aux deux canaux, il vous suffira d'utiliser un numéro d'Unit de 0100 lors de votre requête pour accéder au seul canal droit, et 0010 pour le gauche. Verrouillage d'un canal Lorsque vous utilisez un canal audio quel qu'il soit, vous avez toujours le risque de vous le faire réquisitionner par une autre tâche plus prioritaire. Ceci peut entraîner l'arrêt brutal du son, ce qui, vous en conviendrez, est quelque peu gênant. Pour résoudre ce problème, deux solutions s'offrent à vous. La première qui vient à l'esprit, consiste à s'allouer le canal avec la priorité maximum (127). Cette solution, bien qu'efficace, fait peu de cas d'un environnement multitâche, d'autant plus qu'une autre tâche peut déjà avoir eu la même idée. Une autre solution, respectant bien mieux l'environnement, consiste à verrouiller le canal. Lorsqu'une tâche 1, de plus haute priorité, tente de s'allouer un canal verrouillé par une tâche 2, ce canal n'est pas libéré, mais la tâche détenant le canal (en l'occurrence 2), reçoit un message lui demandant de le libérer au plus vite. La commande de verrouillage est ADCMD_LOCK. Je ne saurais trop vous conseiller de verrouiller le canal, juste après son allocation, afin d'éviter tout problème. Mais n'oubliez pas de le déverrouiller dès que n'en avez plus besoin. Les deux fonctions ci-dessous vous permettront de verrouiller et déverouiller un canal audio, alloué précédemment. ![]() Nous terminerons par un descriptif de quelques autres commandes de contrôle de la sortie sonore. Ces commandes sont les suivantes :
|