Bienvenue, Invité. Merci de vous connecter ou de vous inscrire.
Avez-vous perdu votre e-mail d'activation ?

Auteur Sujet: SFML pour l'émulation  (Lu 3121 fois)

0 Membres et 1 Invité sur ce sujet

yo_fr

  • Newbie
  • *
  • Messages: 9
    • Voir le profil
SFML pour l'émulation
« le: Décembre 28, 2012, 09:07:43 pm »
Salut à tous,

Je suis en cours d'écriture d'un émulateur de vieux bouzin (Hector de la gamme Micronique à base de Z80). J'ai, au départ, voulu savoir si le VB.Net était suffisamment rapide pour programmer ce genre de chose qui au début ne pouvait fonctionner que par du code C ou asm; mais maintenant je le sais : Ca marche !

L'ensemble de l'émulateur fonctionne (Z80, Mémoire, I/O, écran, clavier, K7, joystick, lecteur de disquette (à base de Z80 séparé et d'un contrôleur) ...) et seule la gestion du son est manquant. j'ai cherché sur le net la meilleur façon de programmer le son et j'ai trouvé SFML...
Mon cahier des charges est le suivant :
* L'émulateur créé un buffer de 20ms de son. Il effectue cela durant environ 7 à 8 ms (en fait c'est l'émulation du Z80 qui bosse et remplit ce buffer, comme sur la vrai machine).
* On attend une synchronisation temporelle de 20 ms
* On envoi dans le stream de son le buffer calculé
 
C'est tout simple MAIS : il ne faut pas de rupture entre le buffer en cours de play et l'entrée des  nouvelles donnée de façon à éviter des "plop" toutes les 20 ms...

J'ai donc créé une class audio en stream comme suit :


Public Class HectorStream
    Inherits SoundStream
    '////////////////////////////////////////////////////////////
    '/// Constructor
    '////////////////////////////////////////////////////////////
    Dim BufferSize As Integer
    Dim myOffset As Integer
    Dim myBuffer(1) As Short

    Sub New(ByVal BufferSize_ As Integer)
        BufferSize = BufferSize_
        myOffset = 0
        ReDim myBuffer(BufferSize)
    End Sub

    Protected Overrides Function OnStart() As Boolean
        OnStart = True
        Dim i As Double
 
        'Creation d'un buffer avec une son sinusoidale
        For i = 0 To BufferSize - 1
            myBuffer(i) = Math.Sin(i * 6.28 / 110) * 32700
        Next
    End Function

    Protected Overrides Function OnGetData(ByRef Data() As Short) As Boolean
        'Remplissage du stream
        Data = myBuffer '
        OnGetData = True
    End Function

    Sub AddSample(ByRef Donnee() As Short)
        '  Futur : remplissage par le Z80, par appel de AddSample()....
        '  myBuffer = Donnee
    End Sub

    Public Sub SomeInitFunction()
        Dim ChannelsCount As Integer = 1
        Dim SampleRate As Integer = 44100
        ' init SoundStream.
        Initialize(ChannelsCount, SampleRate)
    End Sub

End Class


ensuite je créé l'instance:

        Son = New HectorStream(882)

(882 : c'est 44100 echantillons par seconde / 50 pour faire les 20ms de play...)


suivi d'une initialisation :

        Son.SomeInitFunction()

puis le play :

         Son.play()



Ce programme me permet de tester le programme en envoyant un son continu (sinusoide) qui devrait donner un son continu, puisque dés de SFML me demande des données, je transmets un buffer contant et prés-documenté...

La problématique : Lors de l'exécution on entend parfaitement... les plop indiquant du buffer empty...
J'ai agrandi le buffer à 44100 (soit 1 seconde...) et c'est pareil (toutes les secondes). De plus il ne me semble pas que le OnGetData() soit réellement scruté toutes les 20ms (ou même toutes les secondes lorsque je mets 44100 échantillons) mais plutôt seulement 3/4 fois au total... J'ai essayé avec un buffer rempli d'un fichier (comme l'exemple) cela semble presque ok (petit plop quand même en fin de buffer, mais nettement moins fort)

Y a t il une façon de faire mieux ?

Je n'attend pas une solution toute programmée, mais juste une "bonne" idée pour la réalisation.
Pour voir l'avance du prog actuel : googélisez "VBHector yo_fr" (forum cfg system). Pour info j'avais déjà programmé dans MESS les machines Hector...en C
JJ ;)
« Modifié: Décembre 28, 2012, 10:41:24 pm par Laurent »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32271
    • Voir le profil
    • SFML's website
    • E-mail
Re : SFML pour l'émulation
« Réponse #1 le: Décembre 28, 2012, 11:03:07 pm »
J'ai testé ton code en version C++, et ça marche impeccablement. J'ai eu des plops uniquement lorsque la taille du buffer n'était pas un multiple de 110, puisque du coup le son n'était pas continu (saut lorsqu'on passe du sample BufferSize - 1 au sample 0). Mais pour 882 normalement le saut devrait être imperceptible, puisque c'est quasiment un multiple de 110.
« Modifié: Décembre 28, 2012, 11:04:48 pm par Laurent »
Laurent Gomila - SFML developer

yo_fr

  • Newbie
  • *
  • Messages: 9
    • Voir le profil
Re : SFML pour l'émulation
« Réponse #2 le: Décembre 29, 2012, 08:29:53 am »
Le code VB serait plus beaucoup plus lent que le code C++ ?
Je n'y crois pas trop. Je vais sortir ce code de mon code d'émulation pour décharger l'applicatif. Je vous tiens au jus. Par contre, théoriquement, je pars 1 semaine sans net...je donnerais des nouvelles un peu plus tard. Merci de l'essai.

Même avec un buffer de 110 octets le son est ok ?
A combien avant la fin du buffer de stream la proc OnGetData est-elle lancée ?

PS : j'avais calculé la fréquence pour que cela soit un multiple... ;)

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32271
    • Voir le profil
    • SFML's website
    • E-mail
Re : SFML pour l'émulation
« Réponse #3 le: Décembre 29, 2012, 09:36:20 am »
Citer
Le code VB serait plus beaucoup plus lent que le code C++ ?
Je n'y crois pas non plus. Tout le boulot est fait côté C++, tout ce qui est exécuté côté VB ce sont les quelques appels à OnGetData, qui ne font pas grand chose.

Citer
Même avec un buffer de 110 octets le son est ok ?
Oui, aucun plop ;)

Citer
A combien avant la fin du buffer de stream la proc OnGetData est-elle lancée ?
SoundStream utilise trois buffers. Donc cela se passe comme ça :
- stream.Play()
  -> remplissage du buffer 1 (appel à OnGetData)
  -> remplissage du buffer 2 (appel à OnGetData)
  -> remplissage du buffer 3 (appel à OnGetData)
  -> lecture du buffer 1
...
- buffer 1 est vide
  -> lecture de buffer 2
  -> remplissage de buffer 1 (appel à OnGetData)
...
- buffer 2 est vide
  -> lecture de buffer 3
  -> remplissage de buffer 2 (appel à OnGetData)
- etc.

Donc pour résumer, OnGetData est appelée à chaque fois qu'un buffer est vide, c'est-à-dire 3 fois quand tu lances le son, puis très exactement toutes les N secondes, où N est la durée d'un buffer.
Laurent Gomila - SFML developer

yo_fr

  • Newbie
  • *
  • Messages: 9
    • Voir le profil
Re : SFML pour l'émulation
« Réponse #4 le: Décembre 29, 2012, 01:52:20 pm »
Merci de ton ajde et des info
jj

yo_fr

  • Newbie
  • *
  • Messages: 9
    • Voir le profil
Re : SFML pour l'émulation
« Réponse #5 le: Décembre 29, 2012, 08:21:37 pm »
J ai cree une appli separee et c est pareil...
Par contre j ai joue avec mes parametres : lorsque je monte le buffervers de grosse valeur (2200 octets) ou que je baisse le samplerate, cela fonctionne !
En gros pour ne plus avoir les buffer low, il faut
Long buff = 2200 a 44100e/s
long buff = 1100 a 22050
Long buff = 600 a 11025

Je vais garder cette valeur pour de  11025e/s pour pouvoir etre a 882 e/ 20ms
merci de l aide et si tu as une solution pour monter en e/s car pour le coup, je suis un peu decu du vb et je ne vois pas pourquoi en c tu peu avoir un buffer aussi court et pas en vb.
Pour info je suis avec un portable i7 a 3,2ghz et 8go de memoire...

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32271
    • Voir le profil
    • SFML's website
    • E-mail
Re : SFML pour l'émulation
« Réponse #6 le: Décembre 29, 2012, 08:51:56 pm »
Il faudrait que je teste directement en .Net à l'occase, c'est quand même super étrange. Je ne sais pas si j'aurais le temps mais j'essayerai.
Laurent Gomila - SFML developer

yo_fr

  • Newbie
  • *
  • Messages: 9
    • Voir le profil
Re : SFML pour l'émulation
« Réponse #7 le: Janvier 02, 2013, 05:58:36 pm »
J'ai mis en place le son mais j'ai pas mal de plop... j'ai du augmenter les buffer à plus de 200ms (10 remplissages de buffer de 20ms) pour obtenir quelque chose d'écoutable... mais c'est pas terrible.
J'ai utilisé des taux d’échantillonnage non classique (c'est plus facile pour calculer des fréquences) et cela marche aussi bien. Par exemple je suis à 10000 e/s et non 11025...
Bref c'est à affiner

yo_fr

  • Newbie
  • *
  • Messages: 9
    • Voir le profil
Re : SFML pour l'émulation
« Réponse #8 le: Janvier 02, 2013, 10:05:05 pm »
Bien  en fait j ai re betonne mon code, notament au niveau du rebouclage et c est maintenant correct.
J avais des zrreurs d arrondi impossible a corriger ( les instruction du z80 emulee n etant pas toutes de meme longueur, j avais parfois un echantillon manquant.
Maintenant que mes calcul de taille sont fait en dur ( nb echant/ sec, taille buffer, temps cycle z80 avant prise echantillon...) j ai remis 44100 hz et c est impeccable !
Merci de l aide.
Les dll necessaire a l execution sont diffusable ? ( je mets a dispo mon code et l executable et donc j aimerais mettre les dll avec)
Merci de l info...
jj

yo_fr

  • Newbie
  • *
  • Messages: 9
    • Voir le profil
Re : SFML pour l'émulation
« Réponse #9 le: Janvier 02, 2013, 10:10:06 pm »
Oups, j oubliais que malgres tout je reste avec un buffer  de 200ms ce qui est genant car du coup on a un delta t entre l execution z80 vu a l ecran et le son produit ( 600ms  c est vraiment important)
Cqfd : je reste preneur d une solution ... ;)

yo_fr

  • Newbie
  • *
  • Messages: 9
    • Voir le profil
Re : SFML pour l'émulation
« Réponse #10 le: Janvier 13, 2013, 12:15:14 am »
Aie,
Je reviens à la charge parce que j'ai besoin d'info : Comment fonctionne le stream ?
J'ai créé mon programme de son dans mon émulateur et cela est loin d'être suffisant car j'ai beaucoup de mélange de son. Pour y voir plus clair, j'ai créé un exemple qui ne devrait que marcher et qui bug un peu et je n'arrive pas à voir pourquoi. Voici mon exemple :


J'ai créé une class toute bête de hérité de sound stream,


Public Class HectorStream
    Inherits SFML.Audio.SoundStream
    '////////////////////////////////////////////////////////////
    '/// Constructor
    '////////////////////////////////////////////////////////////
    Dim BufferSize As Integer
    Dim myBuffer(1) As Short ' (A redimentionner dynamiquement)
    Public Flag As Boolean = False ' Indique que le gestionnaire de son a demandé des données
    Dim SampleRateH As Integer

    Sub New(ByVal BufferSize_ As Integer, ByVal SampleRate_ As Integer)
        BufferSize = BufferSize_ - 1  ' (à partir de 0 sur la longueur demandée)
        ReDim myBuffer(BufferSize)
        SampleRateH = SampleRate_
    End Sub
    Protected Overrides Function OnStart() As Boolean
        OnStart = True
    End Function
    ' Protected Overrides Sub onSeek(ByVal Time As System.TimeSpan)
    ' '   onseek = False
    ' End Sub
    Protected Overrides Function OnGetData(ByRef Data() As Short) As Boolean
        'Remplissage du stream
        Data = myBuffer
        Flag = True
        OnGetData = True
    End Function

    Sub AddSample(ByRef Donnee() As Short)
        ' Récupération du buffer calculé
        myBuffer = Donnee.Clone
    End Sub

    Public Sub SomeInitFunction()
        Dim ChannelsCount As Integer = 1
        ' init SoundStream.
        Initialize(ChannelsCount, SampleRateH)
    End Sub

End Class


et je l'initialise avec des données simple :

      Son = New HectorStream(LG_Sample, SampleRate)

Avec LG_Sample = 882*2 (2 buffer de 20ms) en 44100Hz de SampleRate

Ensuite je créé deux buffers pour avoir 2 notes :

        Dim buffe(LG_Sample - 1) As Short
        Dim buffe2(LG_Sample - 1) As Short


        Dim Freq As Double
        Freq = (2 * Math.PI * 1000 / SampleRate)
        'Initialisation du buffer avec un sin
        For i = 0 To LG_Sample - 1
            buffe(i) = Math.Sin(i * Freq) * 32700
            buffe2(i) = Math.Sin(i * Freq * 1.5) * 32700
        Next
 


Quelques init encore :

      ' Initialisation
        Son.SomeInitFunction()

        Son.Loop = False
        Son.Volume = 100.0

        'On lance le play
        Son.Play()

        Son.Flag = False
 


Et enfin on lance le son :

        ' Remplissage des buffers
        For i = 1 To 10
            Do Until Son.Flag
                Application.DoEvents()
            Loop
            Son.Flag = False
            If (i Mod 2) = 0 Then
                Son.AddSample(buffe)
            Else
                Son.AddSample(buffe2)
            End If
        Next i

Puis pour finir, j'arrête le son (en fait je vide le buffer de la class pour que le son ensuite joué soit nul):

      For i = 0 To LG_Sample - 1
            buffe(i) = 0 ' Math.Sin(i * Freq * 0.5) * 32700
        Next

        Do Until Son.Flag
            Application.DoEvents()
        Loop
        Son.AddSample(buffe)
        Son.Flag = False

Roulement de tambour.... Cela devrait donner
1764 échantillon d'une note et 1764 échantillon de l'autre note, et cela 5 fois !

Evidement ce n'est pas ce que j'ai !

Voici le son obtenu :

* 1764 échantillons note 1 (correct)
* 4000 échantillons à 0 (?)
* 1764 échantillons note 1 (correct)
* 1764 échantillons note 2 (correct)
* 3528 échantillons note 1 (?)
* 1764 échantillons note 2 (correct)
* 1764 échantillons note 1 (correct)
* 1764 échantillons note 2 (correct)
* 3528 échantillons note 1 (?)
* 1764 échantillons note 2 (correct)
* 1764 échantillons note 1 (correct)
* 1764 échantillons note 2 (correct)
* 3528 échantillons note 1 (?)
* 1764 échantillons note 2 (correct)
* 1764 échantillons note 1 (correct)
* 1764 échantillons note 2 (correct)
* 3528 échantillons note 1 (?)
* 1764 échantillons note 2 (correct)
* 1764 échantillons note 1 (correct)

* 3528 échantillons à 0
* 1764 échantillons note 1 (?)


Bref le son est extrêmement mélangé, contrairement à notre attente  :10 * (note 1, note 2) de 1764 échantillons chacun...)


Je me pose donc pas mal de question :
* Pourquoi j'ai une erreur au début avec un trou de 4000 échantillons ? (on est loin de 3528 qui pourrait se comprendre)
* pourquoi la note 1 est assez souvent jouée 2* ? (1 fois sur 2 pour être précis)
* Pourquoi l'arrêt ne se fait il pas de manière brute ?

L'un des réponse pourrait se trouver dans les mélanges de threads. Néanmoins je ne pense pas, les actions étant immédiates, suite à la détection de consommation du buffer (via le set du Flag) le nouveau buffer et réactualiser pour attendre 20*2ms... Cela devrait laisser le temps à tout le monde d'y arriver.

Méthode de test :
1) Pour info j'enregistre avec GoldWave le son produit par l'ordinateur avec un taux d'échantillonnage de 44100Hz, ensuite il suffit de compter les samples d'une fréquence ou d'une autre.

2) Le programme de test est tel que reproduit ici. Il est inclus dans mon programme global d'émulation, mais je ne lance aucun autre thread ou calcul lors de ces essais.


Merci d'avoir tout lu ! et la raison qui fait que je me trompe serait un plus !!!

Merci d'avance,
Jean-Jacques

yo_fr

  • Newbie
  • *
  • Messages: 9
    • Voir le profil
Re : SFML pour l'émulation
« Réponse #11 le: Janvier 13, 2013, 06:27:00 pm »
... En fait le programme fonctionne bien; c'est toujours le même problème de temps de latence quelque part : Lorsque le buffer augmente en taille (et donc en temps) ca marche de mieux en mieux. Le mini semble être 4*20ms soit 80ms. Ce qui fait un décalage temporelle de 240ms (3 buffers d'écart entre la trame en cours de rendu et le son joué, suite à l'envoi u précédent buffer).

C'est beaucoup, et cela se sent très bien dans les jeux d'actions qui tournent sur mon émulateur. Par contre pour pouvoir synchroniser correctement, je suis obligé de terminer les boucles de fonctionnement du processeur par une attente de fin de play du buffer. Ceci dit c'est pas plus gênant que cela à 44100hz, attendre le changement de buffer tous les 80ms se réalise tous les 3528 échantillons joués...( à savoir que les interruptions ont lieu toutes les 20ms et donc les interruption intermédiaire sont elles gérées par attente temporelle réelle (en ms) et seul 1 sur 4 attend la fin du buffer son.
Avec ce type de synchro, je n'ai plus mes fameux plop  ;)

JJ