Obligement - L'Amiga au maximum

Vendredi 30 mai 2025 - 04:01  

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 SMUS
(Article écrit par Pascal Amiable et extrait d'Amiga News Tech - août 1992)


Suite logique de l'étude du format 8SVX, cet article concerne le format de gestion des partitions musicales sur Amiga. Il s'agit du format SMUS qui est utilisé par presque tous les logiciels du commerce, dont le célèbre Deluxe Music.

Le format SMUS a été créé pour pouvoir composer et jouer des morceaux de musique "classique". Cela signifie que les évènements musicaux ont été encodés pour respecter le format classique musical (partitions, mesures, clefs, notes...). De par le faible nombre de types d'évènements différents apparaissant dans une partition, le codage des informations est très compact et il ne faut que très peu de place mémoire pour stocker une oeuvre musicale importante.

A cette notion d'évènement ("event" en anglais) se superpose une autre notion essentielle, celle de piste (track). Une piste musicale (terme très utilisé dans le monde des synthétiseurs) décrit une suite d'évènements musicaux en fonction du temps. Cette suite est linéaire et en aucun cas deux évènements ne peuvent se superposer : il s'agit soit d'une note unique, soit d'un accord dont les différentes notes qui le composent débutent et se terminent au même moment.

Ces différentes pistes peuvent ensuite être superposées pour former une partition polyphonique où plusieurs instruments peuvent jouer simultanément. Bien entendu, le nombre maximum de pistes superposables dépend directement, pour leur restitution, du nombre de canaux sonores disponibles sur l'ordinateur.

Ainsi, Deluxe Music restituera un maximum de quatre canaux directement sur l'Amiga, alors qu'il pourra en restituer 16 sur un synthétiseur via l'interface MIDI. Le format SMUS en prévoit jusqu'à 255.

Chaque piste est stockée dans un bloc de données TRAK. Le nombre de pistes est indiqué dans un bloc de données SHDR (Score HeaDeR) qui se trouve au début du fichier. Ces blocs de données TRAK sont stockés de manière ordonnée (1, 2, 3...). Il s'agit d'un ordre des priorités, la piste la plus importante étant jouée en premier.

Ainsi, un programme de restitution pouvant jouer par exemple quatre pistes, restituera les quatre premières et ignorera simplement les suivantes.

Les instruments

Une autre notion importante associée au format SMUS concerne la gestion des instruments. L'instrument est le moyen de restitution des notes présentes sur la piste. Afin de déterminer quel instrument va être utilisé pour restituer une note, il est nécessaire de définir la notion d'instrument courant. Si aucune commande spécifique n'est passée, l'instrument utilisé est l'instrument par défaut qui est défini grâce au bloc de données INS1 (voir ci-dessous). Sinon, on pointera vers un registre d'instrument, registre qui peut être utilisé par plusieurs instruments simultanément.

Les données associées aux instruments dépendent de l'environnement de restitution (Amiga ou synthétiseurs MIDI). Dans tous les cas, il est nécessaire de rendre la partition indépendante de cet environnement. C'est pour cela qu'est apparue cette notion de registre d'instrument. Ce registre contient le nom de l'instrument, et pointe à son tour vers ses données réelles, quel que soit leur type. La gestion de ces données est laissée au programme de restitution.

Le schéma ci-dessous résume la manière dont sont gérés les instruments dans le format SMUS.

C
Gestion des instruments

Les blocs de données FORM et SHDR

Associé au bloc de données FORM (décrit dans les précédents articles), on trouve le bloc de données SHDR définissant les données de base de la partition. La structure ScoreHeader associée au bloc de données est la suivante :

C

Le champ "tempo" représente le tempo initial du morceau à jouer et s'applique à toutes les pistes. Il s'exprime en 128e de noires par minute. Ainsi, un tempo de 1 représente une noire toutes les 128 minutes, alors que 12 800 représente 100 noires par minute. Ce mot de 16 bits représente en fait un nombre décimal réel, les neuf bits de poids fort représentant la partie entière et les sept bits de poids faible la partie décimale. Ce tempo global peut être modifié en cours de morceau grâce à une marque dynamique SEvents que l'on trouve dans la description d'une piste.

Le champ volume représente le volume de sortie du morceau, qui est compris entre 0 et 127. Ce volume peut également être modifié grâce à un ordre SEvents.

Enfin, le champ "ctTrack" indique le nombre de pistes différentes que possède le morceau de musique.

Les blocs de données NAME, (c), AUTH, ANNO

Les blocs de données NAME, (c), AUTH et ANNO sont identiques à ceux utilisés pour le format 8SVX et ne seront pas décrits en détail dans cet article. Voici pour mémoire leurs déclarations.

C
C

Le bloc de données INS1

Ce bloc de données identifie l'instrument qui va être utilisé par le morceau. Il s'agit d'un champ optionnel qui peut être ignoré par le programme de restitution (le lecteur). La structure du bloc de données est la suivante :

C

Ce type de bloc de données associe à un numéro de registre instrumental (champ "register"), un nom d'instrument (champ "name[]"). Il peut y avoir jusqu'à 255 registres musicaux dans une partition, mais en général, seuls quelques-uns sont utilisés. Afin d'aider à la localisation d'un instrument MIDI référencé par ce bloc de données, il existe deux nombres, data1 et data2. Si le champ "type" vaut "INS1_Name", alors on cherche l'instrument par son nom et data1 et data2 ne servent à rien (on les initialisera à 0). Si type vaut "INS1_MIDI", alors on devra rechercher l'instrument ayant pour preset data2 sur le canal MIDI data1.

Il est à noter que le bloc de données INS1 remplace le bloc de données INST qui devient donc obsolete et hors norme.

Le bloc de données TRAK

Le principal contenu de la partition SMUS est stocké dans les bloc de données TRAK, qui représentent les pistes de la partition. Il doit y avoir au moins autant de blocs de données TRAK dans le fichier IFF que de pistes dans la partition.

Le contenu d'un bloc de données TRAK est représenté par un tableau de mots de 16 bits définissant chacun un évènement (note, silence, changement d'instrument, de tempo, de volume...). Cet évènement représente une série d'ordres qui sont exécutés en séquence. Un bloc de données TRAK peut être polyphonique, c'est-à-dire contenir des accords en tenant compte des restrictions énoncées ci-dessus.

Chaque évènement est stocké dans un enregistrement SEvent (pour Simple musical Event). Chaque enregistrement est constitué d'un champ type codé sur 8 bits et représentant le type d'évènement à exécuter. Le second champ représente, quant à lui, les données associées à la commande.

Ce format est ainsi extensible et permettra dans le futur de définir de nouveaux évènements. Deux évènements doivent toutefois être compris par n'importe quel programme, ce sont les évènements "note" et "rest".

C
C

Avant d'entamer la réalisation d'un programme d'exemple, il est nécessaire de détailler deux SEvents qui nous intéressent plus particulièrement : Note et Rest.

Ces deux évènements possèdent la même structure, ce qui est normal compte tenu de leur similitude.

C

Cette implémentation sous forme de champs de bits "Unsigned :n" est compactée bits à bits, du poids le plus fort au plus faible, pour totaliser comme toutes les structures SEvents, 16 bits au total.

Certains compilateurs interprétant mal cette forme de structure bits à bits vous pouvez la remplacer par deux UBYTES et gérer vous-même le deuxième en tenant compte du schéma explicatif ci-dessous.

C
Détermination du type de note

Le programme d'exemple

Le programme que nous allons réaliser restitue une partition au format SMUS en utilisant des instruments au format 8SVX, instruments qui sont bien entendu spécifiés dans le fichier SMUS. Les fichiers 8SVX seront décodés sous la forme d'une série d'échantillons étalée sur huit octaves. Si ces derniers n'étaient pas enregistrés sur cette base, les octaves manquant seraient remplacés en utilisant les plus proches disponibles.

Une table de périodes de douze éléments donne la vitesse de restitution pour un octave, couvrant ainsi les douze tons de ce dernier.

La première partie du programme d'exemple correspond au main(). Elle contient la trame du lecteur ainsi que les déclarations des variables globales.

/*------------------------------------------*/
/*       Player IFF SMUS V 1.0              */
/*       (c) ANT 1992                       */
/*------------------------------------------*/

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

#define SMUS MakeID('S','M','U','S')  /* chunk form SMUS */
#define SHDR MakeID('S','H','D','R')  /* chunk ScoreHeader décrivant les données de base */
#define INS1 MakeID('I','N','S','1')  /* chunk de description des instruments */
#define TRAK MakeID('T','R','A','K')  /* chunk décrivant une piste */
#define I8SVX MakeID('8','S','V','X')  /* chunk form 8SVX */
#define VHDR MakeID('V','H','D','R')  /* chunk VoiceHeader pour l'instrument */
#define BODY MakeID('B','O','D','Y')  /* chunk des données associées à l'instruments */

#define LIMIT_INSTRUMENT 8

struct GfxBase    *OpenLibrary(); /* Ouverture des bibliothèques */
struct GfxBase    *GfxBase; /* Pointeur sue la bibliothèque graphique */
struct Message    *GetMsg(); /* Récupération de message */
struct MsgPort    *CreatePort(); /* Création d'un port */
struct IORequest  *CreateExtIO(); /* Création du timer */ 
struct Task       *FindTask(); /* recherche de taches */
struct FileHandle *Open(),     /* pour l'ouverture du fichier */
                  *inshandle;   /* gestion d'un instrument     */
       APTR       AllocMem();  /* pour l'allocation memoire   */
       ULONG       notes();     /* jouer les SEvents           */
       void       finaudio();  /* Arret du morceau            */
       ULONG      initaudio();  /* initialisation audio        */
       ULONG      chargeinst(); /* chargement d'un instrument  */
       void       referme();   /* desallocation générale      */
       LONG       arret(); /* gestion de l'arret user     */
       void      dechinst(); /* dechargement d'un instrument */
       ULONG     calcul(); /* calcul de la note */

/*------------------------------------------*/
/*  Définition des variables globales       */
/*------------------------------------------*/

struct FileHandle *smusfichier; /* pointeur sur le fichier SMUS */
UBYTE             *sdonnee; /* donnees associées au fichier SMUS */
ChunkHeader       *smusheader; /* header SMUS */
SEvent            *piste[8]; /* tableau des pistes */
LONG               tcompteur; /* compteur de piste */

LONG              icompteur, horloge; /* compteur et constante d'horloge */
UBYTE             *idonnee;           /* pointeur vers les données du fichier 8SVX */
Voice8Header     *instheader; /* header d'un instrument 8SVX */
Chunk             *instchunk; /* chunk d'instrument */
LONG              taillefich,numinst,scompteur;
UBYTE             *sechant,*sechants[LIMIT_INSTRUMENT]; /* pointeur vers les blocs d'échantillons */
LONG              *ptabptr, *ptabptrs[LIMIT_INSTRUMENT],staille,stailles[LIMIT_INSTRUMENT],longueur[16*LIMIT_INSTRUMENT];
BYTE              *echant[16*LIMIT_INSTRUMENT];

/*LONG              frequence[] = { 41860,44540,46986,49780,52740,55860,
                              59200,62720,66448,70400,74586,79022};*/
struct IOAudio    *ioaud[20]; /* 2 attaques, 2 parties principales, */
struct Message    *msg;          /* 1 descente x 4 canaux = 20 */
struct MsgPort    *port[4];  /* port de gestion de message pour les ioauds */
struct MsgPort    *tport[4];
ULONG             device[4]; /* device */
struct timerequest *treq[4]; /* timers */
ULONG             timer[4];

UBYTE inreg[4];
UBYTE canal1[] = {1};
UBYTE canal2[] = {2};
UBYTE canal3[] = {4};
UBYTE canal4[] = {8};
UBYTE *canaux[] = {canal1,canal2,canal3,canal4};

ULONG ttable[] = {26880,13440, 6720, 3360, 1680,  840,  420,  210,
                  40320,20160,10080, 5040, 2520, 1260,  630,  315,
                  17920, 8960, 4480, 2240, 1120,  560,  280,  140,
                  26880,13440, 6720, 3360, 1680,  840,  420,  210,
                  21504,10752, 5376, 2688, 1344,  672,  336,  168,
                  32256,16128, 8064, 4032, 2016, 1008,  504,  252,
                  23040,11520, 5760, 2880, 1440,  720,  360,  180,
                  34560,17280, 8640, 4320, 2160, 1080,  540,  170};

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


LONG main(argc,argv)
LONG argc;
char *argv[];

{

SEvent        *sevent;
RefInstrument *instref;
SScoreHeader  *partheader;
Chunk         *chunk;
char          *nom,instnom[60],iobuffer[8];
LONG          i,lcompteur,compteur,gamme;
ULONG         etat;

onbreak(&arret);

/* On commence par tester la validité des instruments d'entrée */

smusfichier = 0;
scompteur = 0;
numinst = 0;
etat = 1;

if(argv[1] == NULL)
   referme("Aucun nom de fichier en entrée\n");

/* On lit le fichier  */

nom = argv[1];
if(!(smusfichier = Open(nom,MODE_OLDFILE)))
   referme("Je n'arrive pas à ouvrir le fichier");

lcompteur = Read(smusfichier,iobuffer,8);
if(lcompteur == -1)
   referme("Problème de lecture du fichier");
if(lcompteur < 8)
   referme("Ce n'est pas un fichier IFF il est trop petit");

/* On analyse le Header */

smusheader = (ChunkHeader *)iobuffer;
if(smusheader->ckID != FORM)
   referme(" Ce n'est pas un fichier IFF\n");
if(smusheader->ckSize > 65536)
   referme("Fichier trop gros\n");

/* On charge le fichier */

if (!(sdonnee = (UBYTE *) AllocMem(smusheader->ckSize, MEMF_PUBLIC | MEMF_CLEAR)))
   referme("Impossible d'allouer de la mémoire\n");

lcompteur = Read(smusfichier,sdonnee,smusheader->ckSize);
if(lcompteur == -1)
   referme("Problème de lecture du fichier");
if(lcompteur < smusheader->ckSize)
   referme("fichier IFF malformé");

/* Evaluation du type de fichier */

if(MakeID( *sdonnee, *(sdonnee+1), *(sdonnee+2) , *(sdonnee+3)) != SMUS)
   referme("Ce n'est pas un fichier IFF SMUS");

/* recherche des différents chunks */

compteur = 4;
tcompteur = 0;

while(compteur < smusheader->ckSize);
   {
   chunk = (Chunk *)(sdonnee+compteur);
   switch(chunk->ckID)
      {
      case SHDR:

         partheader = (SScoreHeader *)(sdonnee+compteur+8L);
         gamme = (8929 / (partheader->tempo >> 7));
         for(i=0;i<64;i++) ttable[i] *= (ULONG)gamme;
         break;

      case INS1:

         instref = (RefInstrument *)(sdonnee + compteur + 8L);
         for(i=0;i <= chunk->ckSize; i++)
            instnom[i] = instref->name[i];
         instnom[chunk->ckSize-4L] = 0;
         if(etat = chargeinst(instnom))
            referme("Impossible de charger un instrument \n");
         break;

      case TRAK:

         if(tcompteur < 4)
            {
            sevent = (SEvent *)(sdonnee+compteur+8L);
            piste[tcompteur] = sevent;
            piste[tcompteur+4] = (SEvent *)(sdonnee+compteur+8L+chunk->ckSize);
            tcompteur ++;
            }
         else
            puts("Les pistes supérieures à 4 ne sont pas prises en compte ...\n");
         sevent = (SEvent *)(sdonnee+compteur+8L+chunk->ckSize);
         break;

      default:
         break;
      }
   compteur += 8L + chunk->ckSize;
   if(chunk->ckSize & 1L == 1)
      compteur ++;
   }

/* on est parti pour jouer la partition */

if(!etat && !numinst)
   {
   if(!(etat = initaudio()))
      notes(piste);

/*  C'est fini */

   finaudio();
   dechinst();
   }

if(smusfichier) Close(smusfichier);
if(sdonnee) FreeMem(sdonnee,smusheader->ckSize);
return(0L);
}

Mise à jour de mai 2025 : une archive contenant le listing adapté à vbcc, et avec l'exécutable compilé par vbcc, a été réalisée par Yann-Gaël Guéhéneuc et est disponible sur obligement.free.fr/files/antsmusplayer.lha.


[Retour en haut] / [Retour aux articles]