Thread concurrency ? (OpenGL vs STK) - C++ - Programmation
Marsh Posté le 21-03-2009 à 12:40:36
D'après ce que j'en comprends et comme son nom semble l'indiquer RtAudio et juste une api bas-niveau temps-réel.
Donc:
- un flux de sortie pour tout le programme donc un openStream et un closeStream en début et fin de programme, hors boucle-temps réel
- ton callback de remplissage de buffer sera par la suite à remplacer par un callback de mixage avec sa petite machine à états et sa queue de buffers à mixer, on verra peut-être à ce moment pour le threading.
Dans ce cas il consisterai je pense à un thread de mixage qui fourni qui pousse un buffer en queue d'attente, et le callback passé à RtAudio qui vienne chercher les buffers mixés en queue.
Donc revois ta structure générale, parceque là c'est pas bon.
Pour moi tu devrais avoir un truc style:
Code :
|
Quand tu sais que tu dois avoir un bruit, tu le mets en queue d'attente, et au prochain callback tu commence à le mixer avec les autres dans le buffer final qui sera passés aux dac par RtAudio.
Etant donné que tu sais pas où tu mets les pieds, je te conseillerai OpenAl ou autre une lib audio de plus haut-niveau pour avoir un truc qui te guide.
Même si par après pour ta culture générale tu peux très bien revenir sur un mixage complètement maison utilisant une API bas-niveau.
Marsh Posté le 21-03-2009 à 23:47:09
bjone a écrit : ...- un flux de sortie pour tout le programme donc un openStream et un closeStream en début et fin de programme, hors boucle-temps réel |
C’est justement le rapport avec la boucle temps réel que je ne saisi pas. J’ai l’impression que, par rapport à ce que je recherche comme la génération et la modulation du son par rapport à des évènements graphiques (ex la rencontre aléatoire de 2 sphères), l’évènement sonore sort de cette boucle puisqu’une fois déclanché à partir de celle-ci, il se produit en dehors par le calcul de sa thread !?
Je dis surement des bêtises mais j’imagine dans mon cas le rôle de la Thread comme une sorte d’écouteur activé et désactivé par des évènements asynchrones (évènements physiques et graphiques)…
Par ailleurs j’ai cru comprendre qu’il fallait éviter d’ouvrir et de fermer systématiquement un stream, mais plutôt jouer sur le contenu d’un buffer circulaire qui pourrait être vide lorsqu’aucun son n’est joué …
bjone a écrit : |
Mis à part ce que tu appelles la « machine à états et sa queue » j’ai 2 autres membres susceptibles de jouer ce rôle :
Code :
|
bjone a écrit : |
Que veux tu dire par threading, car le callback c’est bien l’aboutissement d’une thread non ?
bjone a écrit : |
Dit comme ça, je n’demande pas mieux ;-)
bjone a écrit : . |
Pour l’instant la partie graphique et physique tourne bien. J’ai pensée à une classe principale (statique) pour les manip globales (gestion des canaux, effets) et à une classe dynamique représentant du son singulier. C’est le code du STK lui-même et la notion de thread qui me perdent encore.
Pour être plus précis sur ma structure générale j’ai:
Define.h // contient des déclaration d’entêtes et descriptions de structures utilisées ds les classes statiques et dynamiques
Une classe principale Engine déclarée dans le main, qui instancie les classes graphiques, physique et sonore dont les membres seront appelés dans une boucle glut :
Code :
|
Comme je n’ai pas pu intégrer toutes mes classes respectives (image, physique…), certaines fonctions sont restées dans le main comme la boucle glut ou le gestionnaire de collisions physiques …
L'articulation des 2 classes, statique et dynamiques me semblait cohérentes jusqu'à ce qu'apparaissent les threads...d'ailleurs
on m'a conseillé de mettre
bjone a écrit : |
En imaginant que le Stream serait ouvert/fermer indépendamment, my_zuper_audiomixing_engine…() pourrait remplir le buffer de nouveaux paramètres audio ?
bjone a écrit : |
A moins qu’il y est un exemple dans le STK je vais faire qlq recherches mais si tu as exemple de queue d'attente ...
bjone a écrit : |
…si j’arrive au premier callback ;-)
bjone a écrit : |
Je pensais que c'était plutôt pour jouer des sons (sampling) que de la synthèse sonore où tu travailles vraiment son signal...
bjone a écrit : |
En fait, j'avais pensé que pour générer un son particulier, par ex une onde faite de plusieurs sinusoïdes modulées par une série de variables(ex : liste de positions) ce serait la meilleur alternative. Maintenant, je sais que ça me dépasse totalement cette histoire, d’autant plus que je commence le c++ très tard, mais bon je reste fasciné par le bas niveau et le plus grand degré de liberté qu'on peut en espérer. De plus, à défaut d'avoir pu suivre des cours, ce sont des gens comme toi qui permettent à d'autre de toucher des choses qu'ils n'aurait jamais eu l'occasion ou le courage...et blablabla, enfin voilà, en tout cas ton analyse m’éclaircit bien les idées et si mes précisions change ta vision du prob … poursuivons
Marsh Posté le 22-03-2009 à 13:08:39
ton callback doit balayer une liste de sons actifs, et générer le buffer que RtAudio consommera.
ça veux dire qu'à l'initialisation de RtAudio, tu n'as qu'un seul openstream, un seul callback, et ce callback balaye une liste de sons que tu auras mis en queue lors de tes événements physiques & co.
ces sons auront comme propriétés, si tu veux mixer des sinus: fréquence, phase, amplitude, balance g/d, durée. (et pourquoi pas une enveloppe ADSR http://en.wikipedia.org/wiki/ADSR_envelope)
tu pousses les descripteurs de ces sons dans une collection qui sera prise en compte et actualisée par le callback.
par exemple quand le callback génére 20ms d'audio, tu retires 20ms de vie aux descripteurs, et supprimes ceux qui ont expirés.
Marsh Posté le 23-03-2009 à 03:49:25
Salut
bjone a écrit : ...tu pousses les descripteurs de ces sons dans une collection qui sera prise en compte et actualisée par le callback. |
Les descripteurs seraient des Structures tandis que la collection pourrait être un Array ?
C'est ce que j'ai essayé de faire plus bas...
D'autre part, on m'a proposé d'utiliser un Buffer circulaire (CircBufferNoLock) mais je n'arrive pas à convertir une Structureen const unsigned char.
bjone a écrit : |
Tu t'y prendrais comment ?
Car si je devais vraiment utiliser CircBufferNoLock , je serais bien embêté pour le vider car il n'y a pas de membre prévu à cet effet; que:
Code :
|
Mon sound system modifié :
Code :
|
Marsh Posté le 23-03-2009 à 14:05:37
Le buffer circulaire est une solution locale pour la gestion des buffers mixés.
Mais là tu pourrais ignorer cette problématique, vu que le callback te fourni déjà le buffer de sortie. (tout dépends si tu veux tourner avec quelques buffers d'avance ou pas au dépends de la latence).
Pour les descripteurs tu pousse un objet qui décrit ton son dans une collection, et le callback supprimera les sons expirés de cette collection.
Donc le choix de l'implémentation tu le fait, tu essayes tu vois.
Marsh Posté le 26-03-2009 à 10:47:36
Salut Bjone
bjone a écrit : Le buffer circulaire est une solution locale pour la gestion des buffers mixés. |
Ah, je croyais qu'il ne pouvais y en avoir qu'un seul
bjone a écrit : Mais là tu pourrais ignorer cette problématique, vu que le callback te fourni déjà le buffer de sortie. . |
ok
bjone a écrit : tout dépends si tu veux tourner avec quelques buffers d'avance ou pas au dépends de la latence. |
très utile ! donc à voir plustard...
bjone a écrit : Pour les descripteurs tu pousse un objet qui décrit ton son dans une collection, et le callback supprimera les sons expirés de cette collection. |
Pour l'instant ce que je comprends, c'est que *samples est un float qui représente l'ensemble des sons.
Du coup j'ai pensé calculer mes sines en incrementant *samples dans une boucle qui récupère le calcul du son de chaque Attack :
Code :
|
Ici, le Callback semble bien appeller chaque Attack, mais j'ai l'impression de n'entendre qu'en seul son.
La fréquence se régle pourtant sur la derniere Attack ?
ps: je me suis basé sur un exemple car cette boucle, qui me paraissait logique :
Code :
|
...provoque une erreur l'allocation mémoire...
Marsh Posté le 26-03-2009 à 11:05:20
A toi de traquer tes bugs.
Par contre pour l'instant tu n'itères pas sur ta liste de sons, tu n'en traite qu'un.
Marsh Posté le 26-03-2009 à 12:16:18
bjone a écrit : A toi de traquer tes bugs. |
bjone a écrit : Par contre pour l'instant tu n'itères pas sur ta liste de sons, tu n'en traite qu'un. |
Alors que fait ?:
Code :
|
J'ai essayé d'intégrer une autre boucle :
Code :
|
...mais c'est pas ça
Comment ferais tu ?
Marsh Posté le 26-03-2009 à 12:52:50
*samples++ = ...
ça c'est pour remplir ton buffer de sortie, sample par sample.
mais par sample, tu dois sommer tes sinusoides.
Marsh Posté le 27-03-2009 à 09:29:07
bjone a écrit : *samples++ = ...ça c'est pour remplir ton buffer de sortie, sample par sample. |
Ok, ça j'avais saisit
bjone a écrit : |
Sommer, sommer, je ne suis pas sûr de comprendre; les appeler tu veux dire !?
En tout cas je me suis posé une question dans le genre en observant l'exemple demo :
Code :
|
Quand il n'y a pas tout les crochets je m'y perd un peu...enfin, par rapport à ce que tu me disais, la réponse semble être ici.Car en plus de *samples, il y a sample; soit un float pour le son global résultant et un autre pour chaque son lu
Enfin , je crois... donc à ma sauce ça donne:
Code :
|
ou encore :
Code :
|
Mais bien sûr, c'est mauvais car je n'arrive pas à l'adapter dans le cas d'une sinWave
...au cas ou il y aurait une erreur ici, Attack.cpp:
Code :
|
Comme j'ai encore bcp à apprendre du c++, peux-tu être plus explicite qd tu dis 'sommer' ?
Marsh Posté le 27-03-2009 à 12:41:25
Sommer: Si tu as plusieurs sons, comment tu crois qu'ils arrivent a tes oreilles ?
Au vu de ça:
Code :
|
Comme le port-salut, data->reverb doit être un objet qui permet de générer de la reverb en rééchantillonnant du son précrédent.
data->wvout[j]->tick(sample) doit certainement prendre un float & en paramètre, et cette boucle somme chaque son à l'échantillon final.
Marsh Posté le 28-03-2009 à 07:37:58
Salut Bjone
bjone a écrit : Sommer: Si tu as plusieurs sons, comment tu crois qu'ils arrivent a tes oreilles ? |
En block, du saint esprit
bjone a écrit : ...de la reverb en rééchantillonnant du son précrédent. |
sample ne représente donc que la réverbe ?
Alors il ne me sert à rien, et en le supprimant je retombe sur ma dernière proposition:
Code :
|
Marsh Posté le 28-03-2009 à 11:48:20
spinzero a écrit : Salut Bjone |
spinzero a écrit : |
spinzero a écrit :
|
regarde la fonction data->wvout[j]->tick(sample), tu sauras comment ils somment. (certainement un += )
Marsh Posté le 28-03-2009 à 20:46:09
bjone a écrit : |
Ben :
wvout.h:
...
Code :
|
wvout.cpp:
Code :
|
Donc computeSample est implémenté d'une manière différente dans chaque ss classe que sont les instruments comme Clarinet, Flute...
Dans mon cas, computeSample de sineWave prend un void en argument:
Code :
|
Ce que j'vois, c'est qu'il boucle sur une plage de valeures table , c-à-d un échantillon de base calculé lors de l'initialisation :
Code :
|
Donc si c'est bien ce que tu voulais dire par [i]Sommer ,
j'ai l'impression qu'ici on ne peut pas ajouter d'effets ou autres traitements ?
De toute façon tout ce que je cherche pour l'instant c'est de faire tourner ensemble ces échantillons...
Alors pourquoi
Code :
|
...ne suffit pas ?
Par ailleurs SineWave::computeSample est un membre protégé, accessible uniquement par la fonction tick() , prenant elle aussi un void en argument, hérité de la classe Generator:
generator.h:
Code :
|
Marsh Posté le 02-04-2009 à 11:58:57
Yep
Finallement j'en suis arrivé à :
Code :
|
J'ai l'impression que l'on somme bien ici toutes les sines
Mais à l'oreille ...
je n'entends que la dernière tonalité; chaque sine ayant une fréquence supérieure à l'antécédente, de manière à pouvoir les distinguer...
Ce qui m'intrigue c'est que je sais pas si ça vient de mon callback audio (ci-dessus) ou des Attacks, qui renvoient pourtant des ticks() différents ??
}
Marsh Posté le 02-04-2009 à 19:00:39
Là je sais pas a toi de voir.
Tu écrêtes probablement, je suppose que RtAudio attends un buffer normalisé, donc ça clippe peut-être.
divise sample par le nombre d'ondes cumulées ou un truc du genre.
Marsh Posté le 03-04-2009 à 06:57:42
Oui, ça règle le problème du volume sonore qui dépassait la bande passante,
thank's doctor
Code :
|
Quand au problème de l'addition foireuse des ondes,
je pense enfin savoir d'où ca vient
En fait si j'utilise, pour commencer, un simple tableau[3] dans la classe qui gère le buffer,
les trois première sines tournent correctement
...petit hic, je n'arrive pas à étendre la capacité de mon tableau.
Code :
|
Donc le problème doit venir de ma manière d'implémenter la classe Attack ...
...ah, finallement l'erreur venait du fait que j'avais pas déclaré SineWave ds le .h d'Attack, c'est tout
Donc les 2 fonctionnent très bien !!!
Marsh Posté le 03-04-2009 à 11:55:12
alors bouquine ça:
std::vector<>, boost::shared_ptr<>, boost::ptr_vector<>, etc..
faut que tu bosses les collections et autre trucs objet en c++, ça va t'aider a refactoriser ton bousin
accessoirement les NxArray<> c'est une solution, mais avoir une dépendance au moteur physique dans l'audio, c'est un peu gore
Marsh Posté le 03-04-2009 à 18:47:31
bjone a écrit : alors bouquine ça: |
Aah, Boost ..!
J'avais pas encore osé mettre de nez dedans...mais l'heure est venue
bjone a écrit : ... les NxArray<> ... une dépendance ...un peu gore |
Aah l'élégance, c'est pas mon fort
ps: merci, merci, merci pour tes conseils; ça booooste !
Marsh Posté le 03-04-2009 à 20:13:18
bin c'est pas interdit de l'utiliser le NxArray, c'est juste un std::vector<>-like de PhysX, mais bon dans l'audio
Marsh Posté le 03-04-2009 à 21:20:15
bjone a écrit : bin c'est pas interdit de l'utiliser le NxArray, c'est juste un std::vector<>-like de PhysX, mais bon dans l'audio |
Oui bien sûr, mais tu me diras " pensez modules indépendants"
Marsh Posté le 01-06-2009 à 14:51:09
bjone a écrit : alors bouquine ça: |
Salut
Voici (http://www.exitframe.net/stk_sample/stk_gl.zip)un exemple utilisant openGl et stk-4.4.0, qui compile sous VC++2005. Pourriez vous y jeter un oeil SVP ?
Le programme plante 3/5 au moment où l'on crée un son
Le code est sans doute maladroit mais je ne trouve pas d'où ça vient...le problème ne doit en tout cas pas venir du type de tableau utilisé.
ps: depuis que j'ai potassé les smart pointer j'ai pu faire un teste avec shared_ptr d'après un exemple( http://arb.developpez.com/c++/raii/shared_ptr/ )mais l'accès au membres de ma classe Attack est plus délicat...enfin j'ai pas encore bien saisit la manip
J'ai récemment découvert ptr_vector après être passé de la version 1.38 à 1.39 -d'ailleurs où est passé le folder lib ds la nouvelle version???- , je n'ai pas encore réussi à l'implémenter mais il semble qu'il soit déconseillé pour la désallocation contrairement au vector classique, donc inutile si je veux pouvoir vider mon tableau !? finallement est-ce qu'un simple array ne serait pas plus efficace ?
merci
Marsh Posté le 19-03-2009 à 09:27:41
Bonjour à tous
Débutant en C++, j'ai une animation (win32) OpenGL à laquelle j'essaie d'intégrer de la synthese sonore avec
la lib STK(http://ccrma.stanford.edu/software/stk/.(au passage, si vous connaissez autre chose, tournant sous windows... )
Mon problème vient du fait que le rendu graphique via une fonction glut:
main.cpp :
est bloqué pendant le calcul du son:
attack.cpp:
... qui semble utiliser une thread , concept que je n'ai pas encore abordé
Je suis donc parti d'un exemple(http://ccrma.stanford.edu/software/stk/crealtime.html) un Callback, dont une portion du code est en partie responsable du problème:
J'ai voulu remplacer cette partie par
...pensant que le son continuerait à jouer mais cela provoque une erreur (error.printMessage(), qui ne s' affiche pas ) , enfin je crois puisque
est aussitôt appelé !
Faudrait-il remplacer le code supprimé par autre chose ou bien est-ce la thread tick() qui pose problème ?
...sachant que Attack est une classe pouvant avoir de multiples instances...
Voilà, j'espère être assez clair
merci
ps: On m'a suggéré de mettre
juste après startStream() , ou dans tick()
... ou encore de m'intéresser au concept de concurrency!??