En avant la musique !

Aujourd’hui, j’ai envie de laisser les players de côté un moment, et d’expérimenter le son de notre console.
C’est dans l’absolu assez simple : 2 voix disponibles et, pour chaque voix, nous pouvons choisir le volume, la forme d’onde (parmi 10 prédéfinies) et la fréquence, c’est à dire la hauteur de la note (par palier).

Trois registres par voix donc :

AUDC0 et AUDC1 = forme d’onde sur 4 bits
AUDV0 et AUDV1 = volume sur 4 bits
AUDF0 et AUDF1  = fréquence sur 5 bits

Ainsi, ce code :

    lda #$0F
    sta AUDV0
    sta AUDF0
    lda #4
    sta AUDC0
        

nous fera un parfait simulateur de mort médicale (biiiiiiiiiip !).
Le souci, c’est que vos tympans vont très vite remarquer que ce son va se poursuivre tant que nous ne l’aurons pas éteint (c’est à dire en mettant 0 dans un des 3 registres) !
Ainsi, pour éteindre un son au bout d’une seconde, nous allons devoir compter le nombre d’images, 60 en ntsc ou 50 en pal.

Vous noterez que la fréquence est codée sur 5 bits, soit 32 valeurs possibles. L’architecture du bestiau fait qu’en fait ce registre ne reçoit pas une fréquence à proprement parler, mais plutôt une simili-note, dépendant de la forme d’onde choisie.
C’est assez complexe, je vous invite à consulter l’excellente page du site RandomTerrain consacrée à ce sujet.

Dans cet exercice, nous allons écrire une routine la plus généraliste possible pour prendre en charge les sons.

Concrètement, qu’est ce qui caractérise une note ? Une fréquence, une durée, un instrument. Dans un premier temps, par facilité, nous considérerons qu’un instrument est simplement une forme d’onde standard.

Il nous faut la liste des notes à jouer et pour chaque note son instrument.
Prévoyons 2 cases mémoire, une pour la position dans la piste, l’autre pour compter les images restantes pour la note en cours

Snd_Pos0       ds 1   ; position dans la piste 
Snd_Counter0   ds 1   ; compteur de frames

Un seul octet pour chaque compteur, cela a pour conséquence que d’une part, la piste à jouer fera maximum 255 notes (bon c’est déjà pas mal, et il sera toujours temps de perfectionner plus tard !), et que d’autre part, une note durera au maximum  4,25 secondes. Nous utiliserons le compteur de position pour allumer/éteindre la piste à jouer, $FF signifiera que c’est terminé tandis que n’importe quelle autre valeur jouera à partir de cette position.

Une note étant codée sur 5 bits, dans un souci de compacité, ne pourrait-on utiliser les 3 bits restants pour la durée ?
En réalité, nous n’avons pas besoin d’expliciter la durée, les 8 valeurs possibles sont bien suffisantes pour représenter une double-croche, une simple-croche, une noire, une blanche, une ronde et une carrée. Si la double-croche dure par exemple 1/8 de seconde, chaque valeur supérieure durant le double de la précédente, nous arrivons à une carrée de 4 secondes, ce qui est notre limite actuellement.

Une note sera donc exprimée comme suit : 5 bits pour la hauteur + 3 bits pour la durée :
%11001000 = double-croche hauteur 25
%11001010 = noire hauteur 25

La définition des durées est enregistrée dans une table, et doit être adaptée au tempo du morceau à jouer :

Snd_Bpm0 
    .byte #7 ; 1/8s en ntsc valeur double croche
    .byte #15 ; 1/4s en ntsc valeur simple croche
    .byte #30 ; 1/2s en ntsc valeur noire
    .byte #60 ; 1s en ntsc valeur blanche
    .byte #120 ; 2s en ntsc valeur ronde
    .byte #240 ; 4s en ntsc valeur carrée

Ensuite, au tour des notes à jouer : 3 notes et un silence

Track0
    .byte #%11100010
    .byte #%11000010
    .byte #%10000010
    .byte #%00000100
    .byte #%00000000 ; boucle

Et enfin, l’instrument correspondant à chaque note :

Instr0
    .byte #4
    .byte #4
    .byte #4
    .byte #4
    .byte #$FE ; boucle

Nous allons également utiliser les instruments pour d’autres fonctions : $FF terminera le morceau, tandis que $FE nous fera boucler.
Cela nous laisse 254 instruments possibles (même si dans un premier temps, nous n’utiliserons que les 10 formes d’onde de base).

Initialisons nos deux compteurs :

   LDA #0  ; démarre de suite
   ;LDA #$FF ; ne démarre pas de suite
   STA Snd_Counter0
   STA Snd_Pos0

et appelons la routine sonore dans la logique du programme pendant le vblank :

    JSR DoSound0
    JMP Wait_VBLANK_End

Au tour de la routine en question :

; --------------------------- Routine de sons --------------------------------    
DoSound0    
    ldx Snd_Pos0
    cpx #$FF ; piste terminée ?
    beq DoSound0_End
    
    lda Snd_Counter0 ; compteur de frame si 0 = nouvelle note
    beq NewNote0
        
    jmp DoSound0_Next
    
NewNote0 ; nouvelle note
    ; instrument
    lda Instr0,x
    cmp #$FF  ; fin de piste ?
    beq StopSound0
    cmp #$FE  ; boucle ?
    beq LoopSound0

    sta AUDC0  ; forme d'onde

    ; nouvelle note
    lda Track0,x
    tax ;2 on sauve A dans X
    lsr ;2 on décale A de 3 bits vers
    lsr ;2 la droite pour obtenir les 
    lsr ;2 5 bits de fréquence
    sta AUDF0  ; "fréquence"
    txa ;2  on récupère A
    and #%00000111 ; et on ne conserve que les 3 bits de droite (durée)
    tax
    
    lda #15    ; volume à fond 
    sta AUDV0  ; 

NewNote0_Next    
    lda Snd_Bpm0,x   ; nombre d'images à compter pour la  
    sta Snd_Counter0 ; durée de la note
    
    inc Snd_Pos0     ; position suivante
    jmp DoSound0_Next

StopSound0     ; fin de piste
    sta Snd_Pos0 ; $FF dans le compteur
    lda #0       ; et on éteint tout
    sta AUDF0
    sta AUDV0
    jmp DoSound0_End

LoopSound0     ; on boucle 
    lda #0  ; compteur de piste au début
    sta Snd_Pos0
    jmp DoSound0
    
DoSound0_Next
    dec Snd_Counter0 ; on décompte une image
    
DoSound0_End
    RTS
; -----------------------------------------------------------------    

Ce programme vous jouera en boucle 3 notes suivies d’un silence.Les améliorations à apporter sont nombreuses : pouvoir choisir la piste à jouer, jouer les 2 voix simultanément, travailler l’enveloppe des sons, optimiser… ce sera bien sûr l’objet d’un futur exercice !

Cet exercice nous a fait mettre en pratique

  • AUDC0 et AUDC1 : forme d’onde
  • AUDV0 et AUDV1 : volume
  • AUDF0 et AUDF1 : fréquence

Vous pouvez télécharger ce fichier source prêt à compiler ici : https://dl.dropboxusercontent.com/u/56947388/Cambouis/bipbip.asm
(click-droit/enregistrer la cible sous)

Publicités

5 responses to “En avant la musique !

  • cyborgjeff

    tu crois que tu pourrais créer un genre de mini interpreteur qui tournerait sous windows, qui ressemblerait visuellement à un genre de « SoundTracker » qui permettrait au final d’avoir un fichier de code intégrable dans un projet Atari ?

  • Cambouis

    Bah dans l’absolu c’est possible ! Maintenant, contrairement à un soundtracker, mon player ne gère pas les patterns, chaque piste avance indépendamment au gré de la longueur des notes, et le plus important, l’atari ne sait pas jouer toute une gamme, les notes sont prédéfinies et spécifiques à l’instrument utilisé. Il est très simple et léger, dans un projet atari, on retirera toujours le superflu pour éviter de consommer du temps cpu et du code en rom. Mais vu la complexité relative pour coder une note et une durée, ce ne serait pas inutile que je développe malgré tout quelque chose un jour.

  • cyborgjeff

    Ceci dit, la manière de composer que tu présentes là-bas ressemble un peu à la manière dont était codé le son dans les jeux PC-Speaker que j’avais réaliser avec mon frangin à l’époque, ou sur l’outil de composition de musique Music Processor que j’avais sur C64, avant de découvrir le tracker.

    L’avantage du système pattern, tracker, peut-être d’optimiser le code afin de pouvoir placer des boucles de bout de séquences audio. Sur le tracker pour C64 que j’ai utilisé pour PowerGlove, ce sont même des patterns par track et ensuite tu définis track par track quel pattern utiliser.

    Je suis bien conscient que les performances musicales de l’Atari sont évidemment limitées, si je suis bien il y a 2 pistes, et il y a déjà les bruitages à y insérer. Cela va souvent se limiter à une séquence mono instrumentale qui va gérer l’ambiance… néanmoins, le défi de la minimalisation musicale me plait beaucoup. On peut déjà donner une ambiance intéressante avec très très peu.

    Comme annoncé sur l’autre post, tu as des idées de projets concrets que tu as envie de faire sur l’Atari autour de toute cet analyse de programmation ?

    • Cambouis

      Sur vcs, je pense que c’est une très mauvaise idée de jouer constamment une musique pendant le jeu, que celui qui n’a jamais joué à Schtroumpfs me jette la première pierre 🙂
      Sinon il y a effectivement 2 voies, et le moyen de faire de jolies choses avec.

      • cyborgjeff

        oui, raison pour laquelle souvent on avait une musique d’intro qui au bout de qq mesures laissait place aux bruitages… sur C64 aussi d’ailleurs.

        sympa sinon ce Schtroumpfs, faudra que je le trouve !

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :