Thread concurrency ? (OpenGL vs STK)

Thread concurrency ? (OpenGL vs STK) - C++ - Programmation

Marsh Posté le 19-03-2009 à 09:27:41    

Bonjour  à tous  :jap:  
 
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 :  
 

Code :
  1. int main()
  2. {
  3. ...
  4. glutReshapeFunc(ReshapeCallback_1);
  5. glutDisplayFunc(RenderCallback_1);
  6. ...
  7. }


 
est bloqué pendant le calcul du son:  
 
attack.cpp:
 

Code :
  1. int Attack::tick( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
  2.          double streamTime, RtAudioStreamStatus status, void *dataPointer )
  3. {
  4.   SineWave *sine = (SineWave *) dataPointer;
  5.   register StkFloat *samples = (StkFloat *) outputBuffer;
  6.   for ( unsigned int i=0; i<nBufferFrames; i++ )
  7.     *samples++ = sine->tick();
  8.   return 1;
  9. }
  10. //--------------------------------------------------------------------------------------------------------
  11. bool Attack::play()
  12. {
  13.    SineWave sine;
  14.   //- Figure out how many bytes in an StkFloat and setup the RtAudio stream.
  15.   RtAudio::StreamParameters p;
  16.   p.deviceId = dac.getDefaultOutputDevice();
  17.   p.nChannels = 1;//_o_->channel;
  18.   RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
  19.   //-The bufferFrames argument is an API-dependent buffering parameter (see RtAudio for further information).
  20.   unsigned int bufferFrames = RT_BUFFER_SIZE;//defined in Stk.h.
  21. //--1) OPEN STREAM
  22.   try {
  23. dac.openStream( &p,            //RtAudio::StreamParameters *outputParameters,
  24.        NULL,           //RtAudio::StreamParameters *inputParameters,
  25.        format,           // RtAudioFormat format, unsigned int sampleRate
  26.        (unsigned int)Stk::sampleRate(), //unsigned int *bufferFrames
  27.        &bufferFrames,       //RtAudioCallback callback, void *userData
  28.        &Attack::tick,        //RtAudio::StreamOptions *options
  29.        (void *)&sine
  30.        );
  31. }
  32.  
  33.   catch ( RtError &error ) {
  34.     error.printMessage();
  35.     goto cleanup;
  36.   }
  37.   sine.setFrequency( _o_->frequency );
  38.   //--2) START STREAM
  39.   try {
  40.     dac.startStream();
  41. }
  42.   catch ( RtError &error ) {
  43.     error.printMessage();
  44.    goto cleanup;
  45. }
  46. // Block waiting here.
  47. /*char keyhit;
  48.   cout << "\nPlaying ... press <enter> to quit.\n";
  49.   cin.get( keyhit );
  50.   -> QUOI METTRE A LA PLACE (ci-dessus) ??  
  51. ****************************************/
  52.  cleanup:
  53.  dac.closeStream();
  54.  //delete dac;
  55.  //_o_->func_killFM(_o_->id);  
  56. }


... qui semble utiliser une thread , concept que je n'ai pas encore abordé  :heink:  
 
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:
 

Code :
  1. // Block waiting here.
  2. char keyhit;
  3.   cout << "\nPlaying ... press <enter> to quit.\n";
  4.   cin.get( keyhit );


 
J'ai voulu remplacer cette partie par
 

Code :
  1. return 1;


 
...pensant que le son continuerait à jouer mais cela provoque une erreur (error.printMessage(), qui ne s' affiche pas ) , enfin je crois  :heink:  puisque  
 

Code :
  1. goto cleanup


 
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  

Code :
  1. return 1;


juste après startStream() , ou dans tick()
... ou encore de m'intéresser au concept de concurrency!??  
 
 

Reply

Marsh Posté le 19-03-2009 à 09:27:41   

Reply

Marsh Posté le 21-03-2009 à 07:57:08    

Apparement ça n'emballe personne :-(

Reply

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 :
  1. int main()
  2. {
  3.    my_zuper_audiomixing_engine.start(); // openStream
  4.    glutReshapeFunc(ReshapeCallback_1);
  5.    glutDisplayFunc(RenderCallback_1);
  6.    my_zuper_audiomixing_engine.close();
  7. }


 
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.

Message cité 1 fois
Message édité par bjone le 21-03-2009 à 12:41:06
Reply

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 :


- 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,


 
Mis à part ce que tu appelles la « machine à états et sa queue » j’ai 2 autres membres susceptibles de jouer ce rôle :
 

Code :
  1. bool Attack::init()
  2. {
  3.  pipe = new CircBufferNoLock(sizeof(_o_)*10);// buffer circulaire
  4. return true;
  5. }
  6. //--------------------------------------------------------------------------------------------------------
  7. //...lorsque la thread audio tourne, on met les données modifiés par les évènements graphiques...
  8. void Attack::gui_callback(float u)
  9. {
  10. audioStruct->frequency = u;
  11. //---( ! ) error C228 left of 'writeBuffer' must have a class/strut/inion !?
  12. //  pipe.writeBuffer(&_o_, sizeof(Attack_o));
  13. }


 

bjone a écrit :


- on verra peut-être à ce moment pour le threading.


 
Que veux tu dire par threading, car le callback c’est bien l’aboutissement d’une thread non ?  
 

bjone a écrit :


-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.


 
Dit comme ça, je n’demande pas mieux ;-)
 
 

bjone a écrit :

.
- Donc revois ta structure générale, parceque là c'est pas bon.


 
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 :
  1. glutDisplayFunc(RenderCallback_1);
  2. void RenderCallback_1()
  3. {
  4. if (gScene && !bPause)
  5. RunPhysics();
  6. // Clear buffers
  7. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  8. ProcessKeys();
  9. GraphicCORE::updateBUTTONSAndSLIDERS();
  10. PhysicCORE::updateCAMERA();
  11. PhysicCORE::updateSHAPES();
  12. PhysicCORE::updateLINES();
  13. PhysicCORE::updateSTICKERS();
  14. RenderScene(bShadows);
  15. if( gSelectedActor != NULL ){
  16.  NxVec3 V= gSelectedActor->getGlobalPosition();
  17.  GraphicCORE::drawAXIS(V.x, V.y, V.z, 10.0F,8.0F,10.0F, 10.0F,6.0F,10.0F,"<X>","<Y>","<Z>" );
  18. }
  19. DisplayText();
  20. gPerfRenderer.render(gScene->readProfileData(true), glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT));
  21. glutSwapBuffers();
  22. //----------------------
  23. //SoundCORE::render();
  24. }


 
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 :


   my_zuper_audiomixing_engine.start(); // openStream
 
   glutReshapeFunc(ReshapeCallback_1);
   glutDisplayFunc(RenderCallback_1);
 
   my_zuper_audiomixing_engine.close();
}


 
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 :


Quand tu sais que tu dois avoir un bruit, tu le mets en queue d'attente,


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 :


 et au prochain callback tu commence à le mixer avec les autres dans le buffer final qui sera passés aux dac par RtAudio.


…si j’arrive au premier callback ;-)
 

bjone a écrit :


... je te conseillerai OpenAl ou autre une lib audio de plus haut-niveau pour avoir un truc qui te guide.


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 :


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.


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   :D  

Reply

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.

Message cité 1 fois
Message édité par bjone le 22-03-2009 à 13:15:13
Reply

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 :


... quand le callback génére 20ms d'audio, tu retires 20ms de vie aux descripteurs, et supprimes ceux qui ont expirés.


 
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 :
  1. bool writeBuffer(const unsigned char *data,  unsigned int len)
  2. bool readBuffer(unsigned char *data,  unsigned int len)
  3. unsigned int getSize();


 
 
Mon sound system modifié :  
 

Code :
  1. //------------
  2. soundCORE.cpp :
  3. //-------------
  4. #include "SoundCORE.h"
  5. static NxArray<Attack *>Attack_A;
  6. int att_i = 0;
  7. CircBufferNoLock* pipe;
  8. RtAudio dac;
  9. typedef struct {
  10.   float frequency;
  11.   float amplitude;
  12. } control_data_t;
  13. ...
  14. ...
  15. bool SoundCORE::init()
  16. {
  17. int t1 = 10;
  18. pipe = new CircBufferNoLock(sizeof(control_data_t)*t1);
  19. return true;
  20. }
  21. void SoundCORE::setRingBUFFER( int id )
  22. {
  23. //---Recherche la Struct relactive au dernier sons
  24.  Attack* fm = NULL;//getFM( id );
  25.  int t1 = Attack_A.size();
  26.  if( t1 != 0)
  27.  {
  28.    for(int i=0;i<t1;i++)
  29.    {
  30.      if( Attack_A[i]->_o_->id == id )
  31.       {
  32.       fm = Attack_A[i];
  33.       break;
  34.       }
  35.    }
  36.  }
  37. //--- ajoute un son (Struct audio)
  38. if( fm != NULL)
  39. {
  40.  control_data_t data;
  41.      data.frequency = fm->_o_->frequency;
  42.      data.amplitude = .1;
  43. /****************************************************
  44. le 1er param de 'writeBuffer' n'accepte que des Const Unsigned Char
  45. Comment convertir data ?
  46. ***************************************************/
  47.  bool pWritten = pipe->writeBuffer( &data, sizeof(control_data_t) );
  48. }
  49. }
  50. int SoundCORE::tick( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *dataPointer )
  51. {
  52. //---  Tout les sons fm sont stockés dans l'array Attack_A
  53.    int t1 = Attack_A.size();
  54.  if( t1 != 0)
  55.  {
  56.    int  j;
  57.    SineWave *sin = (SineWave *) dataPointer;
  58.    register StkFloat *samples = (StkFloat *) outputBuffer;
  59.   /*******************************************************
  60.   Comment adapter 'samples' en fonction des multiples sons (Attack) ?
  61.   C'est sans doute ici que 'pipe' devrait intervenir !?
  62.   ********************************************************/
  63.    for ( unsigned int i=0; i<nBufferFrames; i++ )
  64.    {
  65.     for( j=0;j<t1;j++)
  66.     {
  67.       *samples++ = Attack_A[j]->TICK();
  68.     }
  69.    }
  70.    //-- à integrer ds la boucle ci-dessus
  71.     /*
  72.   Attack_o   o;
  73.   float  frequency;
  74.     if (pipe.readBuffer(&o, sizeof(Attack_o))  
  75.     {
  76.    frequency = o->frequency;
  77.     }
  78.     */
  79.  }
  80. return 0;
  81. }
  82. //---------
  83. Attack.cpp:
  84. //---------
  85. #include "Attack.h"
  86. SineWave sine;
  87. Attack::Attack( Attack_o  * _o )
  88. {
  89. _o_ = _o;
  90. sine.setFrequency( _o_->frequency );
  91. SoundCORE::setRingBUFFER(_o_->id);
  92. }
  93. ...
  94. StkFloat Attack::TICK()
  95. {
  96. return sine.tick();
  97. }
  98. ...


Message édité par spinzero le 23-03-2009 à 04:12:35
Reply

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.

Reply

Marsh Posté le 26-03-2009 à 10:47:36    

Salut Bjone  :p  
 

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 :
  1. int SoundCORE::tick( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *dataPointer )
  2. {
  3.  register StkFloat *samples = (StkFloat *) outputBuffer;
  4.  TickData *data = (TickData *) dataPointer;
  5.  if( attack_T != 0 )
  6.  {
  7.    int i, counter, nTicks = (int) nBufferFrames;
  8.     while ( nTicks > 0 )
  9.     {
  10.     counter = min( nTicks, data->counter );
  11.     data->counter -= counter;
  12.     for( i=0; i<counter; i++)
  13.     {
  14.      *samples++ = attack_A[i]->TICK();
  15.      nTicks--;
  16.     }
  17.    }
  18.   }
  19. return 0;
  20. }


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 :
  1. for ( unsigned int i=0; i<nBufferFrames; i++ ){
  2. *samples++ = attack_A[j]->TICK();


...provoque une erreur l'allocation mémoire...

Reply

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.

Reply

Marsh Posté le 26-03-2009 à 12:16:18    

bjone a écrit :

A toi de traquer tes bugs.


 :jap:  
 

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 :
  1. *samples++ = attack_A[i]->TICK();


 
 
J'ai essayé d'intégrer une autre boucle :
 

Code :
  1. ...
  2. TickData *data = (TickData *) dataPointer;
  3. int i, j,counter, nTicks = (int) nBufferFrames;
  4. while ( nTicks > 0 )
  5. {
  6.              counter = min( nTicks, data->counter );
  7. data->counter -= counter;
  8. for( i=0; i<counter; i++)
  9. {
  10.  for ( j=0; j<attack_T; j++ )
  11.  {
  12.   *samples++ = attack_A[j]->TICK();
  13.   nTicks--;
  14.  ...


 
...mais c'est pas ça  :sweat:  
Comment ferais tu ?

Reply

Marsh Posté le 26-03-2009 à 12:16:18   

Reply

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.

Reply

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 :


... par sample, tu dois sommer tes sinusoides.


 
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 :
  1. int tick( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
  2.           double streamTime, RtAudioStreamStatus status, void *dataPointer )
  3. {
  4.   TickData *data = (TickData *) dataPointer;
  5.   register StkFloat sample, *samples = (StkFloat *) outputBuffer;
  6.   int counter, nTicks = (int) nBufferFrames;
  7.   while ( nTicks > 0 && !done ) {
  8.     if ( !data->haveMessage ) {
  9.       data->messager.popMessage( data->message );
  10.       if ( data->message.type > 0 ) {
  11.         data->counter = (long) (data->message.time * Stk::sampleRate());
  12.         data->haveMessage = true;
  13.       }
  14.       else
  15.         data->counter = DELTA_CONTROL_TICKS;
  16.     }
  17.     counter = min( nTicks, data->counter );
  18.     data->counter -= counter;
  19.     for ( int i=0; i<counter; i++ ) {
  20.       sample = data->volume * data->reverb->tick( data->voicer->tick() );
  21.       for ( unsigned int j=0; j<data->nWvOuts; j++ ) data->wvout[j]->tick(sample);
  22.       if ( data->realtime )
  23.         for ( int k=0; k<data->channels; k++ ) *samples++ = sample;
  24.       nTicks--;
  25.     }
  26.     if ( nTicks == 0 ) break;
  27.     // Process control messages.
  28.     if ( data->haveMessage ) processMessage( data );
  29.   }
  30.   return 0;
  31. }}


 
 
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 :
  1. for( i=0; i<counter; i++)
  2. {
  3.  SineWave* sn = attack_A[i]->getSINE();
  4.  sample =  data->volume * sn->tick();// ca paraît absurde d'appeller tick() ici !?
  5.  for ( unsigned int j=0; j<attack_T; j++ )
  6.  {
  7.   sn = attack_A[j]->getSINE();
  8.   // dans l'autre exemple tick() prend 'sample' comme argument, qui est un StkFrames
  9.   // mais ds mon cas SineWave ne possède pas de membre acceptant d'argument!
  10.   //
  11.   sn->tick();//sample;//frames;//
  12.  }
  13.   *samples++ = sample;
  14.   nTicks--;
  15. }


 
ou encore :
 

Code :
  1. for( i=0; i<counter; i++){
  2.               for ( unsigned int j=0; j<attack_T; j++ ){
  3.  sample = attack_A[j]->TICK();
  4. }
  5.  *samples++ = sample;
  6.  nTicks--;
  7. }


 
Mais bien sûr, c'est mauvais car je n'arrive pas à l'adapter dans le cas d'une sinWave  :heink:  
 
 ...au cas ou il y aurait une erreur ici, Attack.cpp:
 

Code :
  1. #include "Attack.h"
  2. SineWave sine;
  3. Attack::Attack( Attack_o  * _o )
  4. {
  5. _o_ = _o;
  6. sine.setFrequency( _o_->frequency );
  7. sine.addPhase(_o_->amplitude);
  8. //SoundCORE::setBUFFER(_o_);
  9. }
  10. Attack::~Attack()
  11. {
  12. }
  13. SineWave* Attack::getSINE()
  14. {
  15. return &sine;
  16. }
  17. StkFloat Attack::TICK()
  18. {
  19. //sine.setFrequency( _o_->frequency );
  20. return sine.tick();
  21. }
  22. void Attack::TICK( const StkFrames& frames, unsigned int channel )
  23. {
  24. ;// sine.tick( &frames );
  25. }
  26. void Attack::KILL()
  27. {
  28. SoundCORE::killFM( _o_->id );
  29. }


 
Comme j'ai encore bcp à apprendre du c++, peux-tu être plus explicite qd tu dis 'sommer'  ?


Message édité par spinzero le 27-03-2009 à 11:15:09
Reply

Marsh Posté le 27-03-2009 à 12:41:25    

Sommer: Si tu as plusieurs sons, comment tu crois qu'ils arrivent a tes oreilles ? :D
 
Au vu de ça:

Code :
  1. sample = data->volume * data->reverb->tick( data->voicer->tick() );
  2. for ( unsigned int j=0; j<data->nWvOuts; j++ )
  3.      data->wvout[j]->tick(sample);


 
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.

Reply

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 ? :D


En block, du saint esprit  :ange:  

bjone a écrit :

...de la reverb en rééchantillonnant du son précrédent.
data->wvout[j]->tick(sample) doit certainement prendre un float & en paramètre, ...


 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 :
  1. for( i=0; i<counter; i++){
  2.               for ( unsigned int j=0; j<attack_T; j++ ){
  3. sample = attack_A[j]->TICK();
  4. }
  5. *samples++ = sample;
  6. nTicks--;
  7. }


Reply

Marsh Posté le 28-03-2009 à 11:48:20    

spinzero a écrit :

Salut Bjone ;)  


 

spinzero a écrit :


En block, du saint esprit  :ange:  


 

spinzero a écrit :


 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 :
  1. for( i=0; i<counter; i++){
  2.               for ( unsigned int j=0; j<attack_T; j++ ){
  3. sample = attack_A[j]->TICK();
  4. }
  5. *samples++ = sample;
  6. nTicks--;
  7. }




 
regarde la fonction data->wvout[j]->tick(sample), tu sauras comment ils somment. (certainement un += :D)

Reply

Marsh Posté le 28-03-2009 à 20:46:09    

bjone a écrit :


regarde la fonction data->wvout[j]->tick(sample), tu sauras comment ils somment. (certainement un += :D)


 
Ben :
 
wvout.h:
...

Code :
  1. // These abstract functions must be implemented in all subclasses.
  2.   // They are used to get around a C++ problem with overloaded virtual
  3.   // functions.
  4.   virtual void computeSample( const StkFloat sample ) = 0;


 
wvout.cpp:

Code :
  1. ...
  2. void WvOut :: tick( const StkFloat sample )
  3. {
  4.   this->computeSample( sample );
  5. }


 
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 :
  1. StkFloat SineWave :: computeSample( void )
  2. {
  3.   // Check limits of time address ... if necessary, recalculate modulo
  4.   // TABLE_SIZE.
  5.   while ( time_ < 0.0 )
  6.     time_ += TABLE_SIZE;
  7.   while ( time_ >= TABLE_SIZE )
  8.     time_ -= TABLE_SIZE;
  9.   StkFloat tyme;
  10.   if ( phaseOffset_ ) {
  11.     tyme = time_ + phaseOffset_;
  12.     while ( tyme < 0.0 )
  13.       tyme += TABLE_SIZE;
  14.     while ( tyme >= TABLE_SIZE )
  15.       tyme -= TABLE_SIZE;
  16.   }
  17.   else {
  18.     tyme = time_;
  19.   }
  20.   lastOutput_ = table_.interpolate( tyme );
  21.   // Increment time, which can be negative.
  22.   time_ += rate_;
  23.   return lastOutput_;
  24. }


 
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 :
  1. ...
  2. SineWave :: SineWave( void )
  3.   : time_(0.0), rate_(1.0), phaseOffset_(0.0)
  4. {
  5.   if ( table_.empty() ) {
  6.     table_.resize( TABLE_SIZE + 1, 1 );
  7.     StkFloat temp = 1.0 / TABLE_SIZE;
  8.     for ( unsigned long i=0; i<=TABLE_SIZE; i++ )
  9.       table_ = sin( TWO_PI * i * temp );
  10.   }
  11.   Stk::addSampleRateAlert( this );
  12. }
  13. ...


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 :
  1. sample = attack_A[j]->TICK();


...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 :
  1. ...
  2. //! Compute one sample and output.
  3.   StkFloat tick( void );
  4.   //! Fill a channel of the StkFrames object with computed outputs.
  5.   /*!
  6.     The \c channel argument should be zero or greater (the first
  7.     channel is specified by 0).  An StkError will be thrown if the \c
  8.     channel argument is equal to or greater than the number of
  9.     channels in the StkFrames object.
  10.   */
  11.   StkFrames& tick( StkFrames& frames, unsigned int channel = 0 );
  12. protected:
  13.   // This abstract function must be implemented in all subclasses.
  14.   // It is used to get around a C++ problem with overloaded virtual
  15.   // functions.
  16.   virtual StkFloat computeSample( void ) = 0;
  17.   StkFloat lastOutput_;


Message édité par spinzero le 28-03-2009 à 21:26:37
Reply

Marsh Posté le 02-04-2009 à 11:58:57    

Yep :)  
 
Finallement j'en suis arrivé à :
 

Code :
  1. for ( unsigned int i=0; i<nBufferFrames; i++ )
  2. {
  3. *samples = 0;
  4. for( int j=0;j<t1;j++)
  5. {
  6. *samples += Attack_A[j]->TICK();
  7. }
  8. samples++;
  9. }


 
       
J'ai l'impression que l'on somme bien ici toutes les sines  :whistle:  
Mais à l'oreille ... :non:  
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 ??
 
     
     
}


Message édité par spinzero le 02-04-2009 à 12:08:12
Reply

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.

Reply

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 :
  1. *samples /= attack_T
  2. samples++;


 
Quand au problème de l'addition foireuse des ondes,
je pense enfin savoir d'où ca vient  :pt1cable:  
 
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  :bounce:  
...petit hic, je n'arrive pas à étendre la capacité de mon tableau.

Code :
  1. NxArray<Attack *>Attack_A;
  2. SineWave inputs[1];
  3. void SoundCORE::genFM( ...)
  4. {
  5. Attack_o* o = new Attack_o;
  6. o->id = att_i;
  7. o->frequency= 220.0 * (attack_T+1) ;
  8. ...
  9. Attack * m = new Attack( o );
  10. Attack_A.push_back( m );
  11. attack_T = Attack_A.size();
  12. att_i++;
  13. //--- Redéfinit foireuse de la taille du tableau  
  14. inputs[attack_T];
  15. inputs[attack_T].setFrequency( 220.0 * (attack_T+1) );
  16. }
  17. int SoundCORE::tick( ... )
  18. {
  19. ...
  20. ...
  21. for( int j=0;j<t1;j++)
  22.   {
  23. //--A) Attack Class
  24.  *samples += Attack_A[j]->TICK(); // foireux, seule la dernière sine joue
  25. //--B) Ici même
  26. *samples +=inputs[j].tick(); //  super, les ondes s'ajoutent ;-)
  27.   }


 
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  :lol:  
Donc les 2 fonctionnent très bien !!!


Message édité par spinzero le 03-04-2009 à 10:33:56
Reply

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 :D
 
accessoirement les NxArray<> c'est une solution, mais avoir une dépendance au moteur physique dans l'audio, c'est un peu gore :D

Message cité 2 fois
Message édité par bjone le 03-04-2009 à 11:57:12
Reply

Marsh Posté le 03-04-2009 à 18:47:31    

bjone a écrit :

alors bouquine ça:
std::vector<>, boost::shared_ptr<>, boost::ptr_vector<>, etc..
... bosses les collections et autre trucs objet en c++, ça va t'aider a refactoriser ton bousin :D


Aah, Boost ..!
J'avais pas encore osé mettre de nez dedans...mais l'heure est venue  :jap:  
 

bjone a écrit :

... les NxArray<> ... une dépendance ...un peu gore :D


Aah l'élégance, c'est pas mon fort  :D  
 
ps: merci, merci, merci pour tes conseils; ça booooste !


Message édité par spinzero le 03-04-2009 à 18:49:01
Reply

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 :D

Reply

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 :D


Oui bien sûr, mais tu me diras " pensez modules indépendants"  :)

Reply

Marsh Posté le 01-06-2009 à 14:51:09    

bjone a écrit :

alors bouquine ça:
std::vector<>, boost::shared_ptr<>, boost::ptr_vector<>, etc..


 
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  :sweat:  
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


Message édité par spinzero le 01-06-2009 à 15:07:03
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed