Obligement - L'Amiga au maximum

Vendredi 29 mars 2024 - 13:12  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

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

Articles in english


Réseaux sociaux

Suivez-nous sur X




Liste des 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,
ALL


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


Galeries

Menu des galeries

BD d'Amiga Spécial
Caricatures Dudai
Caricatures Jet d'ail
Diagrammes de Jay Miner
Images insolites
Fin de jeux (de A à E)
Fin de Jeux (de F à O)
Fin de jeux (de P à Z)
Galerie de Mike Dafunk
Logos d'Obligement
Pubs pour matériels
Systèmes d'exploitation
Trombinoscope Alchimie 7
Vidéos


Téléchargement

Documents
Jeux
Logiciels
Magazines
Divers


Liens

Associations
Jeux
Logiciels
Matériel
Magazines et médias
Pages personnelles
Réparateurs
Revendeurs
Scène démo
Sites de téléchargement
Divers


Partenaires

Annuaire Amiga

Amedia Computer

Relec


A Propos

A propos d'Obligement

A Propos


Contact

David Brunet

Courriel

 


Programmation : C - Lecteur 8SVX
(Article écrit par Pascal Amiable et extrait d'Amiga News Tech - mai 1992)


Peut-on réellement parler de l'audio.device et du son Amiga en général sans aborder le format IFF 8SVX ? A notre avis, non.

Je vous propose donc une étude rapide du format 8SVX avec pour but la réalisation d'un programme permettant de faire jouer des échantillons à ce format par votre machine favorite. Mais commençons par un petit peu de théorie.

Le composant audio de l'Amiga contient quatre convertisseurs de type DAC (Digital to Analogic Converter) qui lui permettent de transformer un son numérique avec une résolution de 8 bits en son analogique. Un échantillonnage sur 8 bits signifie que la courbe sonore est décomposée, suivant la fréquence de numérisation, en échantillons pouvant prendre 256 valeurs possibles. C'est certes assez faible, mais néanmoins bien suffisant pour des applications personnelles. A titre d'exemple, la résolution d'un bon synthétiseur est de 12 bits et celle d'un CD est de 16 bits (65 536 niveaux possibles).

Généralités

Sur Amiga, les échantillons musicaux sont généralement stockés au format IFF 8SVX. Il s'agit du format standard pour les fichiers 8 bits. Ce format IFF autorise le stockage d'échantillons (ou "samples" en anglais) dont la taille peut atteindre 2 Mo de longueur.

Malheureusement, ces deux mégaoctets de données ne sont pas restituables directement par l'Amiga sans un travail préalable. En effet, tous les registres du composant audio de l'Amiga sont de type 16 bits (y compris le registre contenant la taille de l'échantillon à convertir). Ce qui signifie que la taille maxi de l'échantillon est limitée à 65 536 mots de 16 bits (taille du registre de données), ce qui nous fait des échantillons de 128 ko au maximum.

Cette limitation matérielle n'était pas une aberration en soit lors de la création de l'Amiga : la mémoire Chip étant alors limitée à 512 ko, une taille d'échantillon sonore de 128 ko semblait raisonnable. Mais à l'heure actuelle, où sur certains modèles, la mémoire accessible par les composants internes est de 2 Mo, cette limite devient gênante.

D'ailleurs, vous avez sans doute dû voir des programmes permettant de jouer des échantillons de taille plus imposante. C'est tout à fait (Thierry) possible : il suffit de découper le gros échantillon en morceaux ne dépassant pas 128 ko et de les faire jouer les uns à la suite des autres, en utilisant un mécanisme de double tampon mémoire pour que la transition soit instantanée et donc indiscernable par l'oreille humaine. Cette fonctionnalité représente d'ailleurs une amélioration du programme que je vous propose ici et dont le mécanisme vous sera expliqué en détail le mois prochain.

Notions de bases sur le format 8SVX

Comme tout format IFF, le format 8SVX possède une certaine structure permettant de définir les données contenues dans le fichier. Le but de ce premier article n'est pas de passer en revue l'ensemble de ces structures (appelées "chunks" en anglais), mais d'étudier les trois qui sont indispensables à la réalisation de ce petit lecteur.
  • FORM : ce premier bloc de données a deux buts. D'abord, sa présence identifie un fichier de type IFF. Ensuite, il contient la taille du fichier à charger.
  • VHDR : correspond à l'en-tête du fichier. Cette en-tête contient des informations complémentaires sur le fichier (sous la forme d'autres blocs de données que nous ignorerons dans cet exemple).
  • BODY : indique le début de la zone des données du fichier.
Voilà, nous en savons assez pour écrire notre programme d'exemple qui pourra jouer n'importe quel échantillon au format IFF 8SVX, pour peu que ce dernier ne dépasse pas 128 000 octets.

Le programme que je vous propose fait appel aux includes IFF de monsieur Commodore. Si vous ne les possédez pas avec votre compilateur, vous pouvez vous les procurer dans le domaine public et plus particulièrement sur la disquette Fish n°185.

Étude du programme

Cette partie va détailler, d'un point de vue structurel notre programme d'exemple. Il explique les différents choix effectués sans s'attarder toutefois sur les aspects techniques qui ont été, pour la plupart, abordés dans les deux numéros précédents.

Notre programme principal va comporter un argument d'entrée qui est le nom du fichier. On déclarera donc main() avec les traditionnels arguments argc et argv, argc contenant le nombre d'arguments reçus et argv un tableau de chaînes de caractères contenant les arguments proprement dits. Le premier test consiste donc à vérifier la présence de cet argument.

Ensuite, on cale la base de temps de restitution sonore sur la fréquence de balayage de l'écran. Afin d'être totalement rigoureux, il a fallu prendre en compte les deux modes de visualisation de l'Amiga - PAL et NTSC - dont la fréquence est différente. Cette information est disponible dans le champ GfxBase->DisplayFlags de la graphics.library. La base de temps est une constante comme indiquée dans la fonction chargehorloge().

L'étape suivante est l'ouverture du fichier à l'aide de la fonction Open() et l'évaluation du type de fichier (IFF ou non). Cette évaluation s'effectue en deux temps :
  • Vérifier que la taille à une taille au moins égale à 8 octets.
  • Vérifier que le premier bloc de données est bien FORM, identifiant un fichier IFF.
A partir de cet instant, on peut allouer une zone mémoire en mémoire Chip (elle doit être accessible par le composant audio de l'Amiga) de la taille précisée dans le champ p8chunk->ckSize.

Une fois cette zone allouée, on copie le fichier en mémoire en vérifiant que la longueur lue correspond bien à la longueur attendue. Sinon, il y a une erreur, le fichier étant sûrement endommagé.

Le fichier IFF est désormais en mémoire mais nous ne savons toujours pas s'il s'agit bien d'un fichier 8SVX. On va donc tester les quatre octets suivants pour savoir s'il s'agit bien du bloc de données MY8S, révelateur de la présence d'un fichier 8SVX.

Enfin, il ne reste plus qu'à décoder le reste du fichier à la recherche des en-têtes (VHDR) et des données proprement dites (BODY). Un fichier 8SVX peut contenir soit un seul échantillon, soit une reproduction du même échantillon sur plusieurs octaves différentes. On trouve cette information dans l'en-tête et plus précisément en faisant l'analyse des deux champs header->oneShotHiSamples et header->repeatHiSamples. Le champs non nul indique la longueur de l'échantillon et son type puisque seul un de ces deux champs peut être non nul.

La vitesse de transfert des informations sonores est ensuite calculée à partir d'une part, de la base de temps vidéo, et d'autre part, de la fréquence d'échantillonnage de l'échantillon. Cette fréquence peut être récupérée dans le champs header->samplesPerSec.

On a donc : vitesse=horloge/header->samplesPerSec.

Enfin, on détermine la priorité de la tâche et on s'alloue le périphérique logique audio comme indiqué dans le numéro précédent. On prépare l'émission des données et une fois celle-ci lancée, il ne reste plus qu'à attendre tranquillement la fin de l'émission en écoutant la douce voix de votre Amiga.

Une fois que tout est fini, on n'oubliera pas de désallouer tout ce qui avait été alloué précédemment et de libérer le périphérique logique audio pour que d'autres puissent l'utiliser. C'est la fonction referme() qui se charge de cela.

/*----------------------------------------------------------*/
/*                                                          */
/*             8SVX Player Version 1.0                      */
/*                                                          */
/*             Par Herr Doktor Von G... pour l'ANT 1992     */
/*                                                          */
/*----------------------------------------------------------*/


/*----------------------------------------------------------*/
/*                                                          */
/*          Declaration des #includes et #defines           */
/*                                                          */
/*----------------------------------------------------------*/

#include <exec/types.h>
#include <exec/memory.h>
#include <devices/audio.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <graphics/gfxbase.h>
#include <iff/iff.h>
#include <iff/8svx.h>

#define VHDR MakeID('V','H','D','R')
#define BODY MakeID('B','O','D','Y')
#define MY8S MakeID('8','S','V','X')

/*----------------------------------------------------------*/
/*                                                          */
/*          Declaration des variables globales et fonctions */
/*                                                          */
/*----------------------------------------------------------*/

struct IOAudio *ioaud1;   /* pointeur sur structure IOAudio */
struct IOAudio *Aptr; 
struct Message *msg;           /* Structure message et port message */
struct MsgPort *port;          /* pour la gestio du device audio */
ULONG          device;         /* le device                      */
UBYTE          *pubfich;         /* deux variables pour le stockage en */
ULONG           pubtaille;          /* PUBLIC RAM pour le traitement     */
UBYTE          *chipfich;         /* deux variables pour le stockage en */
ULONG          chiptaille;          /* CHIP RAM de l'échantillon          */

struct FileHandle *gestfich;   /* la structure de gestion du fichier */


UBYTE canal1[] = {1};   /* La table de répartition des canaux */
UBYTE canal2[] = {2};   /* un seul est alloué sans préférence */
UBYTE canal3[] = {3};
UBYTE canal4[] = {4};
UBYTE *canal[] = {canal1,canal2,canal3,canal4};

APTR   OpenLibrary();
APTR   AllocMem();
struct FileHandle *Open();
struct Task *FindTask();
struct MsgPort *CreatePort();
struct Message *GetMsg();
ULONG chargehorloge();
void referme();

/*----------------------------------------------------------*/
/*                                                          */
/*          Programme principal                             */
/*                                                          */
/*----------------------------------------------------------*/

LONG main(argc,argv)

LONG argc;         /* nombre d'arguments d'entrées */
char *argv[];      /* tableau des arguments d'entrées */

{

ULONG horloge;
char *nomfich;
UBYTE *donnee;
BYTE iobuffer[8];
BYTE *echant[2];
ULONG length[2];
Chunk *p8Chunk;
Voice8Header *pVoice8Header;
ULONG y,compteur,vitesse;
ULONG wakebit;
BYTE ancpri,c;
struct Task *mt;

/* initialisation des variables globales */

nomfich = 0L;
device = 1L;
pubfich = 0L;
chipfich = 0L;
ioaud1 = 0L;
gestfich = 0L;

/* test des arguments d'entrée (le nom du fichier) */

if(argv[1] == NULL)
   {
   referme("aucun nom de fichier donné\n");
   exit(1);
   }
nomfich = argv[1];   /* On récupère le nom du fichier */

/* Initialisation de la base de temps en fonction de l'affichage */

horloge = chargehorloge();  

/* Ouverture du fichier */

gestfich = Open(nomfich,MODE_OLDFILE);
if(gestfich == 0)
   {
   referme("Impossible d'ouvrir le fichier\n");
   exit(1);
   }

/* Lecture des 8 premiers octets et évaluation du type de fichier */

compteur = Read(gestfich,iobuffer,8); /* On  lit les 8 premiers octets */

if(compteur == -1) 
   {
   referme("Impossible de lire le fichier\n");
   exit(1);
   }
if(compteur < 8)
   {
   referme("Fichier trop court\n");
   exit(1);
   }

p8Chunk = (Chunk *)iobuffer;

/* on teste s'il s'agit bine d'un fichier IFF qui possède */
/* obligatoirement la directive FORM en tête de fichier   */

if(p8Chunk->ckID != FORM)  
   {
   referme("Ce n'est pas un fichier IFF\n");
   exit(1);
   }

/* allocation d'une zone mémoire pour stockage du fichier */
/* Cette zone doit obligatoirement être en CHIP RAM       */

pubfich = (UBYTE * )AllocMem(pubtaille=p8Chunk->ckSize,MEMF_PUBLIC|MEMF_CLEAR);

if(!pubfich)
   {
   referme("Allocation mémoire impossible\n");
   exit(1);
   }
donnee = pubfich;

/* Lecture du fichier et stockage en RAM */

compteur = Read(gestfich,donnee,p8Chunk->ckSize);

if(compteur == -1) 
   {
   referme("Impossible de lire le fichier\n");
   exit(1);
   }

/* On teste si le fichier est correct à son descripteur */

if(compteur < p8Chunk->ckSize)
   {
   referme("Fichier IFF trop court\n");
   exit(1);
   }

/* Evaluation du type IFF pour déterminer si il est ou non de type 8SVX */

if(MakeID(donnee[0],donnee[1],donnee[2],donnee[3]) != MY8S)
   {
   referme("Ce n'es pas un fichier IFF 8SVX\n");
   exit(1);
   }

/* Evaluation des chunk 8SVX */

donnee = donnee + 4;
while(donnee < pubfich+pubtaille)
   {
   p8Chunk = (Chunk *)donnee;

   switch(p8Chunk->ckID)
      {
      case VHDR:  /* On vient de trouver un Header */
         pVoice8Header = (Voice8Header *)(donnee+8L);
         break;

      case BODY:  /* On découvre le début d'un bloc de données */
         echant[0] = (BYTE *)(donnee + 8L);
         echant[1] = echant[0] + pVoice8Header->oneShotHiSamples;
         length[0]  = (ULONG)pVoice8Header->oneShotHiSamples;
         length[1]  = (ULONG)pVoice8Header->repeatHiSamples;
         break;
      
      default:
         break;
      }

   donnee = donnee + 8L + p8Chunk->ckSize;

   if(p8Chunk->ckSize&1L == 1)
      donnee++;
   }

/* si length[0] = 0 -> multioctave sinon monooctave */

if (length[0] == 0) 
   y = 1;
else
   y = 0;

if(length[y] <= 128000)
   {
   chiptaille = length[y];
   if(length[0] == 0 && length[1] == 0)
      {
      referme("Problème de décodage du fichier \n");
      exit(1);
      }
   chipfich = (UBYTE *)AllocMem(chiptaille,MEMF_CHIP|MEMF_CLEAR);
   if(chipfich ==0) 
      {
      referme("Pas de memoire chip disponible\n");
      exit(1);
      }
   CopyMem(echant[y],chipfich,chiptaille);
   echant[y] += chiptaille;
   }
else
   {
   referme("Sample trop gros\n");
   exit(1);;
   }

/* calcul de la fréquence de restitution du son gràce au balayage video */
/* et à la fréquence d'écanaltillonage indiquée dans le fichier */

vitesse = horloge / pVoice8Header->samplesPerSec;

/* Mise en place de la priorité */

mt = FindTask(NULL);
ancpri = SetTaskPri(mt,21);

/* Allocation du canal audio */

ioaud1 = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_CHIP|MEMF_PUBLIC|MEMF_CLEAR);
if(!ioaud1)
   {
   referme("Impossible d'allouer le canal audio\n");
   exit(1);
   }

/* Création du port de données */

port = CreatePort(0,0);
if(!port)
   {
   referme("Pas de port disponible\n");
   exit(1);
   }
c = 0;

while(device)
   {
   ioaud1->ioa_Request.io_Message.mn_ReplyPort = port;
   ioaud1->ioa_Request.io_Message.mn_Node.ln_Pri = 128;
   ioaud1->ioa_AllocKey = 0;
   ioaud1->ioa_Data = canal[c];
   ioaud1->ioa_Length = 1;

/* Ouverture du device audio */

   device = OpenDevice("audio.device",0,ioaud1,0);
   c++;
   }

/* Si device vaut zéro cela signifie qu'aucun canal n'est disponible */

if(device)
   {
   referme("Pas de canal disponible\n");
   exit(1);
   }

/* Initialisation des blocs IO Audio pour la restitution du sample */

ioaud1->ioa_Request.io_Command = CMD_WRITE;
ioaud1->ioa_Request.io_Flags   = ADIOF_PERVOL;

/* Valuation du volume */

ioaud1->ioa_Volume = 50;

/* de la periode de restitution */

ioaud1->ioa_Period = (UWORD)vitesse;
ioaud1->ioa_Cycles = 1;

/* Enfin des données à jouer */

ioaud1->ioa_Data = (UBYTE *)chipfich;

Aptr = ioaud1;

/* On envoie la purée, on a vérifié avant la taille pour qu'elle  */
/* n'excède pas 128000 octets                                     */

ioaud1->ioa_Length = length[y];
BeginIO(ioaud1);
wakebit = 0L;
wakebit = Wait(1 << port->mp_SigBit);
msg = GetMsg(port);
referme(" ");
return(0L);
}

/*----------------------------------------------------------*/
/*                                                          */
/*        chargement de la base de temps                    */
/*                                                          */
/*----------------------------------------------------------*/

ULONG chargehorloge()

{

struct GfxBase *GfxBase;

GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0L);
if(!GfxBase) 
   {
   puts("Je ne peux pas ouvrir la library graphics !\n");  
   exit(1);
   }
if(GfxBase->DisplayFlags & PAL)
   {
   CloseLibrary(GfxBase); 
   return(3546895L);
   }
if(!(GfxBase->DisplayFlags & PAL))
   {
   CloseLibrary(GfxBase); 
   return(3579545L);
   }
}

/*----------------------------------------------------------*/
/*                                                          */
/*         Fonction de désallocation des ressources         */
/*                                                          */
/*----------------------------------------------------------*/

void referme(message)

char *message;
{
puts(message);

if(gestfich)      Close(gestfich);
if(pubfich)         FreeMem(pubfich,pubtaille);
if(chipfich)         FreeMem(chipfich,chiptaille);
if(!device)        CloseDevice(ioaud1);
if(port)          DeletePort(port);
if(ioaud1)  FreeMem(ioaud1,sizeof(struct IOAudio));
}

Ainsi se termine cette première partie de l'étude du format 8SVX, vous êtes maintenant en mesure de jouer n'importe quel échantillon audio sur un des canaux de l'audio.device. Le mois prochain, nous verrons comment reproduire un son d'une taille supérieure à 128 ko et en stéréo s'il vous plaît. En attendant, je vous souhaite un excellent mois et à bientôt.


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