Pygame 6: Générons des notes musicales

Dans cet article nous nous occuperons de générer des notes musicales. Nous utiliserons aussi toutes les techniques que nous avons appris jusqu’à ici (ouverture de fenêtre pygame, gestion des événements, dessin de figure). On ajoutera le fonctions en python et on dessinera un cercle au lieux d’un rectangle, pour varier et pour apprendre quelque chose de nouveau.

Je n’expliquerais pas la dynamique de la génération du son ni le maths que sont notamment nécessaires. Mais j’en parlerai ici en quelque mots. Ce qu’on va à faire c’est de
générer une séquence de valeurs que suivent une onde sinusoïdale (en réalité deux séquences, car on générera le son en stéréo), à la fréquence d’une note musicale à
choisir entre DO-4 (261,63 Hz) et DO-5 (523.25 Hz).

Combien de valeurs il nous faudra: ça dépends de la fréquence échantillonnage, c’est à dire de combien de fois par seconde il faut fournir une valeur et de la durée du son qu’on produira.

Pour la première fois nous utiliserons des fonctions. Ces dernières sont  une instrument très puissant que nous permet de réutiliser des morceaux de code (des “sous-programmes”), en donnant les quelque paramétrés qui changent. Par exemple, ici on donnera à la fonction joue_note une note musicale de 0 a 7 (de DO-4 a DO-5) et on donnera la même aussi à la fonction dessin_figure qui nous dessinera un cercle dont note représentera la couleur.

Exercise 1: Modifiez le code de l’article précédent, pour générer une note aux hasards pour chaque carré dessiné sur l’écran.

import pygame
from pygame.locals import *

import math
import numpy

taille = (800, 600)

bits = 16
# les "channels" ici ne sont pas comme en 
# http://www.pygame.org/docs/ref/mixer.html#pygame.mixer.get_num_channels

pygame.mixer.pre_init(44100, -bits, 2)
# Initialisons la plateforme de jeux 
pygame.init()

# creeons une fenetre de jeux normale 
gameDisplay = pygame.display.set_mode((800, 600))

# Notre titre pour le jeux...
pygame.display.set_caption("Produisons des notes musicales!")

duree = 0.2          # en secondes

# Frequences audio des notes musicales - ref: https://pages.mtu.edu/~suits/notefreqs.html 
FREQUENCY_DO  = 261.63
FREQUENCY_RE  = 293.66
FREQUENCY_MI  = 329.63
FREQUENCY_FA  = 349.23
FREQUENCY_SOL = 392.00
FREQUENCY_LA  = 440.00
FREQUENCY_SI  = 493.88
FREQUENCY_DO2 = 523.25

# On les arrange dans une list pour faciliter l'utilisation.  
frequences = [FREQUENCY_DO, FREQUENCY_RE, FREQUENCY_MI, FREQUENCY_FA, FREQUENCY_SOL, FREQUENCY_LA, FREQUENCY_SI, FREQUENCY_DO2]
# et on le fait aussi pour les couleurs. 
couleurs   = [Color("azure4"), Color("blanchedalmond"), Color("brown"), Color("darkblue"), Color("darkgreen"), Color("darkorange"), Color("darkred"), Color("deeppink")]  

# Frequence d'echantillonage 
echantillonage = 44100

# Nombre d'echantillons 
echantillons = int(round(duree*echantillonage))

# One met a zero un array d'echantillons - le type est intier à 16 bits
buf = numpy.zeros((echantillons, 2), dtype = numpy.int16)
    
max_echantillon = 2**(bits - 1) - 1


# Fonction: ceci sert a jouer un son...
# Les maths sont un peu compliqué, n'y pensez pas pour le moment...
def joue_note(note):

    for s in range(echantillons):
        t = float(s)/echantillonage   # en seconds
        buf[s][0] = int(round(max_echantillon*math.sin(2*math.pi*frequences[note]*t)))        # gauche
        buf[s][1] = int(round(max_echantillon*0.5*math.sin(2*math.pi*frequences[note]*t)))    # droite
        
    # Produit Un son. 
    son = pygame.sndarray.make_sound(buf)
    son.play(loops = 0)

# Fonction: ceci sert a dessinner une figure geometrique en couleur... 
def dessin_figure(surface, note):
    pygame.draw.circle(surface, couleurs[note], (400, 300), 50, 0)

# Condition de sortie 
sortieDuJeux=False

while not sortieDuJeux :

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            _running = False
            break
        
        if event.type == pygame.KEYDOWN:
            note = -1
            
            if event.key==K_1:
                note = 0
            if event.key==K_2:
                note = 1
            if event.key==K_3:
                note = 2 
            if event.key==K_4:
                note = 3 
            if event.key==K_5:
                note = 4 
            if event.key==K_6:
                note = 5
            if event.key==K_7:
                note = 6
            if event.key==K_8:
                note = 7            
            print(event) 
            
            if note > -1 :     
                # on joue la note correspondant à la touche 
                joue_note(note)
                # on dessin une figure en couleur 
                dessin_figure(gameDisplay, note)
            
    pygame.display.update()       

pygame.quit()

Vous trouverez la référence pour les fréquences des notes musicales ici.