Latence de l' OSC dans Blender
Sommaire
- 1 Définition: Wikipedia EN
- 2 Description du symptôme
- 3 Explications : Le Blender Game Engine pulse
- 4 Des solutions
- 4.1 1 - Solution avec une initialisation de la connexion à chaque pulsation dans Blender
- 4.2 2 - Solution simple : fréquence d'envoi < fréquence de réception
- 4.3 3 - Définir la taille du Buffer UDP
- 4.4 4 - Vider le buffer si il contient plus de 1 valeur
- 4.5 5 - Définir la taille du Buffer UDP avec Pure Data
- 5 Construire un patch pd correct
- 6 Ressources
Définition: Wikipedia EN
Le buffer UDP est adressé en 16 bits. 2 puissance 16 = 65536 bits soit 8192 octets. Quelques octets sont occupés par des datas d'identification. Par exemple, une image 80 x 80 pixels avec un entier court entre 0 et 99 comme variable occupe 80 x 80 x 2 x 4 bits = 51200 bits. Le maxi serait 65000 / 2 x 4 = 8125 pixels soit 90 x 90 = 8100 pixels ! Avec 2 entiers courts possible sur chaque pixel ( ce n'est pas une image en couleurs ).
Description du symptôme
La latence peut aller jusqu'à 15 secondes ! L'action se déroule a une vitesse normale mais décalée dans le temps de 15 secondes. Après une déconnexion du serveur, Blender tourne encore pendant 15 secondes donc reçoit des datas pendant 15 secondes !
Explication : Le tampon UDP (Buffer UDP)
Merci à dfelinto pour l'explication dans le post #5 sur blenderartists.org. Le code qu'il propose est bon à une erreur d'indentation près.
Si l'envoi de data se fait à une fréquence supérieure à la lecture, le Buffer se remplit et les datas qui sont lues dans ce buffer ne sont pas les dernières envoyées.
C'est un problème de baignoire, de robinet et d'écoulement: Si le débit de sortie est plus faible que le débit d'entrèe, le niveau monte.
En anglais, c'est une Queue LIFO : Last In First Out
Latence normale des programmes et des dispositifs
Par exemple, Microsoft annonce une latence de 130 ms avec la Kinect.
Explications : Le Blender Game Engine pulse
Pourquoi ce débit de sortie faible ? Blender est plutôt lent, il pulse à la fréquence que vous définissez avec un maxi de 60 fps, soit une période optimale possible de 16 ms. Il suffit d'envoyer avec une période de 10 ms pour remplir le Buffer. Pure Data peut envoyer avec une période de 10 ms sans problème.
Le test ci-dessous montre des compteurs avec des valeurs de pulse de 0, 1 et 2.
En mode "Pulse", f définit le nombre de frames pendant lesquels la sortie n'est pas activée. Le tableau ci-dessous donne les fréquences et périodes, si le Blender Game Engine est à 60 fps.
Des solutions
1 - Solution avec une initialisation de la connexion à chaque pulsation dans Blender
Si la connexion est réinitialisée à chaque pulsation de Blender, cela vide le Buffer. Mais Pure Data se déconnecte avec cette solution, cela marche avec OSCeleton et le wiiOSC de Julian.
2 - Solution simple : fréquence d'envoi < fréquence de réception
Le tableau ci-dessous donne les fréquences et périodes dans Blender et Pure Data.
La solution la plus rationnelle est de régler la fréquence d' envoi plus basse que la fréquence de réception dans Blender, idem est régler le "timer" dans pure data à une valeur supérieure à la période dans Blender. Le tableau ci-dessus donne la période en fonction du "Pulse Mode" si Blender est à 60 "fps"! Bourrer Blender ne le fera pas aller plus vite !
Régler le "Pulse" à 0 sur tous les Actuators peut sembler efficace, mais cela va probablement faire chuter le "fps" de Blender, si le jeu est un peu lourd.
3 - Définir la taille du Buffer UDP
GameLogic.socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1024)
Ce Fichier:OSC buffer mini 2.49.zip utilise cette option, si elle est commentée le cube est en retard.
4 - Vider le buffer si il contient plus de 1 valeur
C'est la méthode proposée par dfelinto. Ce script a été testé avec un patch avec un timer de 10 ms. Sans vider le buffer, la latence est de 15 secondes, avec ce script il n' ya plus de latence. Bien sûr ce script peut sans doute être amélioré.
import GameLogic
import socket
import OSC
# Get controller and owner
controller = GameLogic.getCurrentController()
owner = controller.owner
# Set init
ip = 'localhost'
data = 0
########################################################################################
def receive_osc(data, port):
try:
# getting first data value from buffer
data, port = GameLogic.socket.recvfrom(1024)
# keep trying to get new data until buffer is empty
try:
trash = data
while True:
data = trash
trash, port = GameLogic.socket.recvfrom(1024)
# print deleted values
print "trash= ", trash
except:
# we force this exception to happen
# that way we know the buffer is empty
pass
except Exception as E:
pass
return data
########################################################################################
# Connect Blender only one time
if not owner['connected']:
owner['connected'] = True
print "Blender Connected"
GameLogic.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
GameLogic.socket.bind((ip, 4951))
GameLogic.socket.setblocking(0)
GameLogic.socket.settimeout(0.02)
# If Blender connected, get osc
else:
# Get hand wii osc from pd
data = receive_osc(data, 4951)
if data != 0:
GameLogic.handWii = OSC.decodeOSC(data)
5 - Définir la taille du Buffer UDP avec Pure Data
bufsize 1024 sur packOSC définit la taille du buffer.
Construire un patch pd correct
Communication entre Pure-data et Blender en OSC propose des exemples pour tester et comprendre.