Obligement - L'Amiga au maximum

Vendredi 24 mai 2019 - 06:01  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · 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 en d'autres langues


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Logiciels
 · Jeux
 · Scène démo
 · Divers


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 : C - Génération de sons (première partie)
(Article écrit par Pascal Amiable et extrait d'Amiga News Tech - mars 1992)


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 :
  • Volume Control Register : ce registre permet d'assigner à chaque canal un volume de sortie, variant entre 0 et 64.
  • Period Register : utilisé pour définir la fréquence d'échantillonnage sonore du canal. Comme il s'agit d'une période et non d'une fréquence, plus la valeur inscrite dans ce registre est élevée, plus la fréquence de sortie sera faible (Fréquence = 2*PI/Période) et par la même, plus le son sera grave.
  • Data Register : le registre de données pointe sur la zone mémoire où sont stockées les données pour ce canal. Ces informations doivent impérativement être stockées en mémoire Chip.
  • Length Register : indique la taille de la zone mémoire où sont stockées les données associées au canal.
Chaque canal audio peut retrouver ses données, soit grâce au 68000, soit à l'aide de quatre canaux DMA (Direct Memory Access). En utilisant les canaux DMA, le système audio peut charger lui-même ses données depuis la mémoire Chip, sans avoir recours au microprocesseur, qui peut alors être utilisé pour d'autres tâches (comme calculer la prochaine onde sonore, par exemple). Pour produire un son, il faut réserver une zone de mémoire dans laquelle on va stocker une représentation numérique de la forme de l'onde sonore. Si l'on utilise exclusivement le composant matériel, il faudra, après avoir rempli cette zone, positionner le volume au niveau désiré, établir la période du son, indiquer au registre de données où se trouve les informations, inscrire la longueur dans le Length Register et activer le DMA.

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.

génération de sons

Une fois cette allocation effectuée, on utilise le pointeur sur le IOAudio Request Block pour le passer à la fonction OpenDevice(). Il faudra ensuite stocker l'adresse du périphérique logique créé, que l'on pourra réutiliser par la suite. De même, lorsque l'allocation à été effectuée, la fonction OpenDevice() a renvoyé une clef d'allocation (ioa_AllocKey) qu'il est nécessaire de connaître pour toutes les requêtes à venir. Voici un court listing résumant les différentes actions à effectuer.

génération de sons

Fonctionnalités de la plate-forme logicielle Audio

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 :
  • Allouer un ou plusieurs canaux pour l'usage exclusif d'une tâche particulière.
  • Établir une priorité d'utilisation, pour autoriser l'émission d'un son important, même si le canal associé est déjà utilisé par une autre tâche.
  • Autoriser une tâche de faible priorité à terminer l'utilisation d'un canal avant qu'il ne soit réquisitionné par une tâche de priorité plus élevée.
  • Autoriser le lancement ou l'arrêt d'un canal, ainsi que la gestion d'une file d'attente pour jouer une série de sons en séquence.
  • Spécifier le type de forme d'onde qui va être joué, ainsi qu'obtenir des informations sur le type de la forme d'onde jouée à un instant donné.
  • Enfin, autoriser l'accès direct au composant audio pour une gestion personnalisée de ce dernier.
Allocation des canaux

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.

génération de sons

Le listing ci-dessous est un exemple de fonction permettant de s'allouer un certain nombre de canaux avec une priorité donnée en même temps que le périphérique logique audio.

génération de sons

Passons maintenant à la deuxième méthode.

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é.

génération de sons

Pour allouer les canaux après l'ouverture du périphérique logique, il faut faire appel à la commande ADCMD_ALLOCATE. La fonction ci-dessous permet d'allouer ainsi les canaux.

génération de sons
génération de sons

Attente d'allocation

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.

génération de sons

Quelques autres commandes

Nous terminerons par un descriptif de quelques autres commandes de contrôle de la sortie sonore. Ces commandes sont les suivantes :
  • CMD_WRITE : insère dans la file d'attente associée à un canal, une forme d'onde. Cette commande peut inclure les données pour la période d'échantillonnage ainsi que le volume de sortie. Par défaut, ce sont les valeurs de la précédente commande CMD_WRITE qui sont prises.
  • ADCMD_PERVOL : change la période d'échantillonnage ou le volume (ou les deux) associée à une forme d'onde qui est en cours d'émission dans le canal de sortie.
  • ADCMD_FINISH : arrête l'exécution de la CMD_WRITE en cours, interrompant ainsi l'émission de la forme d'onde, soit immédiatement, soit à la fin du cycle en cours.
  • CMD_STOP : arrête temporairement l'émission du canal de sortie.
  • CMD_START : redémmarre l'émission sur le canal de sortie (interrompu au préalable par un CMD_STOP).
  • CMD_FLUSH : retire toutes les formes d'onde à jouer qui se trouvent dans la file d'attente associée à un canal de sortie.
  • CMD_RESET : restaure tous les registres audio à leur état initial. Cette commande affecte tous les canaux de sortie, à l'exception des éventuels verrouillés.
  • CMD_READ (synchronisation) : renvoie un pointeur sur une structure IOAudio vous informant des canaux actuellement en train de jouer.
  • ADCMD_WAITCYCLE (synchronisation) : bloque la tâche jusqu'à l'accomplissement de la commande CMD_WRITE en cours. Vous devez utiliser la fonction DoIO() pour envoyer cette commande au périphérique logique.
En utilisant ces deux dernières commandes, une tâche peut aisément se synchroniser avec la sortie audio. Cette possibilité peut être très utile pour synchroniser des sons avec des actions graphiques, par exemple (émission d'un son lorsqu'un objet heurte un obstacle, etc.).


[Retour en haut] / [Retour aux articles] [Article suivant]