pyxen.Music
Une piste audio en streaming attachée à une Entity en tant que composant.
La musique est décodée à la demande, ce qui la rend adaptée aux pistes longues comme la musique de fond et l’ambiance.
Pour les concepts audio, consultez Audio.
Constructeur
Music(filename, *, volume=1.0, pan=0.0, loop=False)
filename est l’unique argument positionnel : le nom d’un asset musical du projet, sans son extension de fichier. Les trois autres sont réservés aux mots-clés (keyword-only) et fixent la valeur de départ de la propriété du même nom (voir Propriétés du composant).
Paramètres
| Paramètre | Type | Description |
|---|---|---|
filename | str | Nom de l’asset musical, sans extension |
volume | float | Gain de départ. 1.0 par défaut |
pan | float | Position stéréo de départ, de -1.0 à 1.0. 0.0 par défaut |
loop | bool | Recommencer à la fin de la piste. False par défaut |
Exemple
theme = Music("theme", volume=0.6, loop=True)
Attacher une Music
Lors du spawn :
bgm = world.spawn(music=Music("theme"))
Ou plus tard :
entity.music = Music("theme")
L’assignation d’une musique lance la lecture immédiatement.
Propriétés du composant
Accès via entity.music :
| Propriété | Type | L/E | Description |
|---|---|---|---|
loop | bool | L/E | Activer la lecture en boucle |
volume | float | L/E | Gain (minimum 0.0) |
pan | float | L/E | Panoramique stéréo (-1.0 à 1.0) |
time | float | L | Position de lecture actuelle (secondes) |
duration | float | L | Durée totale (secondes) |
finished | bool | L | True lorsque la lecture est terminée |
channels | liste | L | Les parties de la piste, chacune avec son propre volume — voir la section Canaux |
beat | int | L/E | Temps écoulés depuis le début de la lecture. L’assignation déplace la lecture — voir Temps musical |
bar | int | L/E | Mesures écoulées depuis le début de la lecture. L’assignation déplace la lecture |
phase | float | L | Avancement dans le temps courant, de 0.0 à 1.0 |
beats_per_bar | int | L | Nombre de temps dans une mesure |
frames_to_beat | int | L | Nombre de frames avant le prochain temps |
tempo | float | L/E | Multiplicateur de vitesse de lecture — voir Tempo |
bpm | int | L | Battements par minute, tels que le fichier les indique |
loop
entity.music.loop = True
Lorsqu’activé, la piste redémarre automatiquement après la fin de la lecture. Usage typique pour la musique de fond.
volume
entity.music.volume = 0.5
Les valeurs inférieures à 0.0 sont limitées à 0.0.
pan
entity.music.pan = -1.0 # full left
entity.music.pan = 1.0 # full right
entity.music.pan = 0.0 # center
Limité à la plage -1.0 à 1.0.
time
t = entity.music.time
Lecture seule. Position de lecture actuelle en secondes.
duration
d = entity.music.duration
Lecture seule. Durée totale de la piste en secondes.
finished
if entity.music.finished:
world.destroy(entity)
Lecture seule. True lorsque la piste a fini de jouer (et que loop est False).
Canaux
music = entity.music
for i in range(len(music.channels)):
print(i, music.channels[i].volume)
music.channels[1].volume = 0.0 # couper la partie 1
Un module tracker ou un fichier MIDI est un ensemble de parties indépendantes jouées à partir d’une seule horloge. music.channels donne une poignée par partie, et chacune a :
| Propriété | Type | L/E | Description |
|---|---|---|---|
index | int | L | Le numéro du canal |
volume | float | L/E | Gain de cette partie seule, limité à 0.0–1.0 |
Chaque canal démarre à 1.0. Comme toutes les parties partagent une seule tête de lecture, en baisser ou en monter une ne dérive jamais par rapport aux autres. C’est le moyen de faire évoluer l’intensité d’une bande-son : faire entrer une partie quand l’action s’accélère, la couper quand le joueur meurt.
Ce qu’est un canal dépend du format :
| Format | Canaux |
|---|---|
| Module tracker | Un par canal du module (un .mod typique en a 4) |
| MIDI | Toujours 16, un par canal MIDI. Un canal que le fichier n’utilise pas reste simplement silencieux |
| OGG | Aucun : len(music.channels) vaut 0 |
La numérotation commence à 0. Demander un canal au-delà du dernier lève IndexError, et on ne peut pas assigner music.channels[i] lui-même, seulement son volume. Un indice négatif compte depuis la fin, comme dans toute liste Python : music.channels[-1] est le dernier canal.
En MIDI, mettre un canal à 0.0 coupe aussi les notes de ce canal qui résonnent encore.
Temps musical
if music.beat != last_beat:
last_beat = music.beat
pulse()
| Propriété | Signification |
|---|---|
beat | Temps entiers écoulés depuis le début de la lecture. Il est monotone : il continue de croître à travers une boucle au lieu de revenir à 0, ce qui permet de le comparer à sa valeur de la frame précédente sans traiter le retour au début |
bar | Mesures entières écoulées depuis le début de la lecture, également monotone |
phase | Position dans le temps courant, de 0.0 jusqu’à (non inclus) 1.0. Là où beat change par paliers, phase est continue : utilisez-la pour tout ce qui doit s’adoucir ou décroître entre deux temps |
beats_per_bar | Nombre de temps dans une mesure |
frames_to_beat | Nombre de frames avant que le prochain temps arrive, arrondi à l’entier supérieur. Servez-vous-en pour lancer quelque chose sur le temps plutôt que juste après |
Assigner beat ou bar déplace la lecture. music.bar = 8 saute au début de la mesure 8 en une seule écriture, tous les canaux restant synchronisés. La frame suivante relit la nouvelle position.
Les compteurs mesurent le temps à partir du fichier ; le format compte donc :
| Format | beat, bar, phase, frames_to_beat, beats_per_bar, bpm |
|---|---|
| Module tracker | Disponibles. Voir ci-dessous |
| MIDI | Disponibles, lus dans le fichier. Voir plus bas |
| OGG | Indisponibles : beat, bar, beats_per_bar, frames_to_beat et bpm valent -1, phase vaut 0.0, et assigner beat ou bar ne fait rien |
Un jeu qui se synchronise sur une musique qui peut être en OGG doit donc vérifier une fois que music.beat >= 0 et se rabattre sur son propre minuteur quand la valeur est -1.
Les temps d’un fichier MIDI
Un fichier MIDI indique lui-même son temps musical, et Pyxen le lit. Un temps est la noire du fichier, bpm et la durée d’un temps suivent les changements de tempo du fichier au fil de la lecture, et beats_per_bar vient de la première signature rythmique du fichier, comptée en noires : 3/4 donne 3, et 6/8 aussi. Un fichier qui n’indique aucune signature rythmique est à quatre temps.
Quand une piste MIDI boucle, le compte passe à la mesure entière suivante : beat et bar continuent d’augmenter, et chaque passage commence sur le premier temps d’une mesure. Un saut tombe exactement sur le temps ; une note qui sonnait déjà avant ce point n’est pas jouée.
Les temps dans un module tracker
Un module n’indique pas de métrique, Pyxen utilise donc la seule définition qu’un module porte : son BPM. Un tick de tracker dure 2.5 / BPM secondes, ce qui rend le BPM du fichier exact précisément quand un temps vaut 24 ticks : quatre lignes à la vitesse 6, ou huit lignes à la vitesse 3. C’est ainsi que Pyxen compte. Compter des ticks plutôt que des lignes garde le temps stable quand le module change de vitesse.
Une mesure compte quatre temps. C’est une convention, pas une donnée : un module en 3/4 indiquera quand même beats_per_bar à 4. Si votre morceau est écrit avec un autre nombre de ticks par temps, le compteur ne correspondra pas à ce que vous entendez.
Tempo
music.tempo = 1.5 # 50 % plus vite, même hauteur
music.tempo = 1.0 # normal
tempo multiplie la vitesse que la piste demande. Rien n’est rééchantillonné, donc la hauteur ne change pas : les parties arrivent simplement plus tôt ou plus tard. bpm continue d’indiquer la valeur propre du fichier quel que soit tempo.
Limites : 8.0 au maximum. Dans le bas, un module tracker ne descend pas sous 0.25 et une valeur qu’il refuse est ignorée (la propriété garde sa valeur précédente) ; le MIDI accepte jusqu’à 0.05. tempo n’a aucun effet sur l’OGG, où il vaut toujours 1.0.
beat, phase et frames_to_beat suivent tempo : un jeu synchronisé sur la musique le reste quand vous l’accélérez.
Exemples complets
Une pulsation à chaque temps
Cet exemple suppose un module tracker nommé theme.mod dans le projet.
from pyxen import *
last_beat = -1
def start():
world.spawn(name="song", music=Music("theme", loop=True))
world.spawn(name="pulse", x=160, y=90,
shape=Circle(24, fill=(1.0, 0.5, 0.2, 1.0)))
def update():
global last_beat
music = world.get(name="song").music
pulse = world.get(name="pulse")
# phase va de 0 à 1 dans chaque temps : le cercle est grand sur le temps, puis se calme.
s = 1.0 + 0.5 * (1.0 - music.phase)
pulse.scale = (s, s)
# beat ne change qu'une fois par temps.
if music.beat != last_beat:
last_beat = music.beat
print("beat", last_beat, "bar", music.bar)
Couper une partie
Cliquez pour couper puis rétablir la deuxième partie de la piste.
from pyxen import *
def start():
world.spawn(name="song", music=Music("theme", loop=True))
def update():
music = world.get(name="song").music
if input.mouse.left.pressed:
part = music.channels[1]
if part.volume > 0.0:
part.volume = 0.0
else:
part.volume = 1.0
Sauter à une mesure
from pyxen import *
def start():
world.spawn(name="song", music=Music("theme", loop=True))
def update():
music = world.get(name="song").music
if input.mouse.left.pressed:
music.bar = 4 # sauter directement à la mesure 4
Format
Un asset musical peut être un fichier OGG Vorbis (.ogg), MIDI (.mid, .midi), ou un module tracker — ProTracker (.mod), FastTracker II (.xm), Impulse Tracker (.it) ou Scream Tracker 3 (.s3m).
L’asset est recherché par nom de base : Music("theme") trouve theme.ogg, theme.mod, ou celle de ces extensions qui existe dans le projet. Passer l’extension est l’erreur la plus fréquente : Music("theme.ogg") cherche theme.ogg.ogg et lève une exception.
Un module tracker pèse souvent quelques kilo-octets là où le même morceau en OGG en pèse plusieurs méga-octets, et il boucle parfaitement par construction — idéal pour un petit jeu.
C’est aussi le format qui offre le plus ici : canaux, temps, mesures et tempo fonctionnent tous sur un module. Le MIDI a les canaux et le tempo ; l’OGG n’a ni l’un ni l’autre (voir les sections Canaux et Temps musical).
Les modules respectent le panoramique : la commande 8xx de ProTracker place un canal dans le champ stéréo pendant que le morceau joue.
Contrairement à Sound (WAV, chargé entièrement en mémoire), Music est streamé et décodé à la demande — mieux adapté pour les fichiers audio longs.
Erreurs courantes
Nom de fichier manquant
Music()
ValueError: Music must have a file
Asset invalide
Music("nonexistent")
Music("theme.ogg") # l'extension est sur le fichier, pas dans le nom
ValueError: Music 'nonexistent' not found. Use the asset name without its extension (.ogg, .mid, .mod, .xm, .it, .s3m).