IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Multithreading Discussion :

[QThread] Probleme lorsque le thread s'execute deux fois


Sujet :

Multithreading

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 42
    Points : 19
    Points
    19
    Par défaut [QThread] Probleme lorsque le thread s'execute deux fois
    Bonjour a tous.

    Voila j'ai un probleme, je me lance a peine dans le multithreading donc je commence juste a comprendre les bases.

    Je voudrais créer un client/serveur qui permette d'envoyer un son. Jusque la pas de probleme ca marche. Mais je me suis dis que pendant que le client joue le son qu'il a recu au lieu de freezer il pourait lui aussi envoyer un son -> d'ou le multithreading.

    Alors ok je connect le signal du client qui informe que des infos arrivent a la creation d'un nouveau thread dans lequel le client effectuera sa lecture.

    Pas de probleme le client ne gele plus. Youpi et il peut meme envoyer un son pendant qu'il en lit un autre.



    Mais voila le probleme si jamais il recoit un autre son alors qu'il est en train dans lire un aïe caramba !!!.

    Du coup je me dis tiens allons voir du coté des mutex mais la l'effet n'est pas escompté le bug n'est pas réparé alors je m'en remet à vous. Je ne sais meme pas si c'est un mutex qu'il faut utiliser en tout cas voila le code :

    D'abord le .h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
     
    #ifndef HEADER_FENCLIENT
    #define HEADER_FENCLIENT
     
     
    #include <AL/al.h>
    #include <AL/alc.h>
    #include <sndfile.h>
    #include <QtGui>
    #include <QtNetwork>
    #include <iostream>
    #include <vector>
     
    class threadEnvoie;
     
    class FenClient : public QWidget
    {
    	Q_OBJECT
     
    	public :
    	FenClient();
    	~FenClient();
    	bool initOpenAl();
    	std::vector<ALshort> loadSound(const std::string& Filename);
    	void shutdownAL();
    	void playSound(std::vector<ALshort> musique);
    	QTcpSocket* getSocket();
    	void donneesRecues();
    	QMutex* getMutex();
     
    	private slots:
    	void connexion();
    	void connected();
    	void deconnexion();
    	void envoiWave();
    	void donneesRecuesThread();
     
    	private :
     
    	QTextEdit* log;
    	QLineEdit* editAdresseIp;
    	QSpinBox* port;
    	QPushButton *boutonConnexion;
    	QPushButton *test;
    	QTcpSocket *socket;
    	int tailleMessage;
    	ALsizei SampleRate;
    	threadEnvoie* tEnvoie;
    	QMutex* mutex;
    };
     
     
    class threadEnvoie : public QThread
    {
     
     public:
      threadEnvoie(FenClient *fen);
      void run();
      FenClient* fenClient;
    };
     
     
    #endif

    Puis le .cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
     
    #include "FenClient.h"
     
    using namespace std;
     
     
    FenClient::FenClient()
    {
     
    	editAdresseIp = new QLineEdit("127.0.0.1");
    	log = new QTextEdit;
    	port = new QSpinBox();
    	port->setMaximum(65536);
    	port->setValue(12012);
     
    	boutonConnexion = new QPushButton("Connexion");
    	QPushButton *boutonQuitter = new QPushButton("Quitter");
    	test = new QPushButton("Test");
    	test->setEnabled(false);
     
    	QGridLayout *layout = new QGridLayout;
    	QGridLayout *layoutEdit = new QGridLayout;
    	layoutEdit->addWidget(log,0,0);
    	layout->setColumnStretch(2,8);
    	layout->setColumnStretch(0,1);
    	layout->setColumnStretch(4,1);
     
     
    	layout->addWidget(editAdresseIp,0,0,1,2);
    	layout->addWidget(port,0,4);
     
    	layout->addWidget(log,1,0,1,5);
    	layout->addWidget(boutonQuitter,2,0);
    	layout->addWidget(test,2,1);
    	layout->addWidget(boutonConnexion,2,4);
     
    	setLayout(layout);
    	socket = new QTcpSocket;
     
    	tailleMessage = 0;
     
    	connect(boutonQuitter,SIGNAL(clicked()),qApp,SLOT(quit()));
    	connect(boutonConnexion,SIGNAL(clicked()),this,SLOT(connexion()));
    	connect(socket,SIGNAL(connected()),this,SLOT(connected()));
    	connect(socket,SIGNAL(disconnected()),this,SLOT(deconnexion()));
    	connect(test,SIGNAL(clicked()),this,SLOT(envoiWave()));
     
    	connect(socket,SIGNAL(readyRead()), this, SLOT(donneesRecuesThread()));
     
    	tEnvoie=new threadEnvoie(this);
    	mutex= new QMutex();
    }
     
    FenClient::~FenClient()
    {
      delete tEnvoie;
      delete mutex;
    }
     
    void FenClient::connexion()
    {
    	log->append(tr("<em>Tentative de connexion en cours ...</em>"));
     
    	socket->abort();
    	socket->connectToHost(editAdresseIp->text(), port->value());
    }
     
    void FenClient::connected()
    {
    	log->append(tr("<em>Connexion reussi vous pouvez parler</em>"));
    	boutonConnexion->setEnabled(false);
    	test->setEnabled(true);
    }
     
    void FenClient::deconnexion()
    {
      test->setEnabled(false);
      boutonConnexion->setEnabled(true);
      log->append(tr("<em>Deconnecté du serveur</em>"));
    }
     
    bool FenClient::initOpenAl()
    {
    	//Ouverture du device
    	ALCdevice* Device = alcOpenDevice(NULL);
    	if(!Device)
    	{
    		cerr<<"impossible d'ouvrir le device"<<endl;
    	        return false;
    	 }
    	//Création contexte
    	ALCcontext *Context = alcCreateContext(Device, NULL);
    	if (!Context)
    	{
    		cerr<<"impossible d'initialiser un contexte"<<endl;
    		return false;
    	}
    	//Activationn du contexte
    	if (!alcMakeContextCurrent(Context))
    		return false;
     
    	return true;
    }
     
    vector<ALshort> FenClient::loadSound(const string& Filename)
    {
     
    	//librairy sndfile
    	//ouverture du fichier audio avec libsndfile
    	SF_INFO FileInfos;
    	SNDFILE* File = sf_open(Filename.c_str(), SFM_READ, &FileInfos);
    	if (!File)
    		cerr<<"impossible d'ouvrir le fichier"<<endl;
    	//Lecture du nombre d'echantillon et taux echantillon
    	ALsizei NbSamples = static_cast<ALsizei>(FileInfos.channels * FileInfos.frames);
    	SampleRate = static_cast<ALsizei>(FileInfos.samplerate);
    	//Lecture des echantillon audio au format 16 bits signe
    	vector<ALshort> Samples(NbSamples);
    	if (sf_read_short(File, &Samples[0], NbSamples)<NbSamples)
    		cerr<<"Impossible"<<endl;
     
    	//fermeture du fichier 
    	sf_close(File);
     
    	return Samples;
    }
     
    void FenClient::shutdownAL()
    {
    	//Récupération du contexte et du device
    	ALCcontext* Context = alcGetCurrentContext();
    	ALCdevice* Device = alcGetContextsDevice(Context);
     
    	//Désactivation du contexte
    	alcMakeContextCurrent(NULL);
     
    	// Destruction du contexte
    	alcDestroyContext (Context);
     
    	//Fermeture du device
    	alcCloseDevice(Device);
    }
     
     
    void FenClient::envoiWave()
    {
     
    	if(!initOpenAl())
    	{
    		cerr<<"impossible d'envoyer le message"<<endl;
    		return;
    	}
     
    	vector<ALshort> sound;
    	sound=loadSound("ocean.wav");
    	QByteArray paquet;
    	QDataStream stream(&paquet,QIODevice::WriteOnly);
    	stream<<static_cast <int> (sound.size());
     
    	for(int i =0;i<(int) sound.size();i++)
    	{
    		stream<<(int)sound[i];
    	}
    	socket->write(paquet);
     
    	shutdownAL();
    }
     
     
     
    void FenClient::playSound(vector<ALshort> musique)
    {
    	if(!initOpenAl())
    	  {
    	    cout<<"impossible d'init"<<endl;
    		return;
    	  }
     
     
     
    	//création tampon
    	ALuint buffer;
    	alGenBuffers(1, &buffer);
     
    	//Remplissage avec les echantillons lus
    	alBufferData(buffer,AL_FORMAT_MONO16, &musique[0],musique.size() * sizeof(ALshort),SampleRate);
     
     
    	if (alGetError() != AL_NO_ERROR)
    		return;
     
     
     
    	//Création de la source
    	ALuint Source;
    	alGenSources(1, &Source);
     
    	//On attache le tampon contenant les echantillons audio a la source
    	alSourcei(Source, AL_BUFFER, buffer);
     
    	//Lecture
    	alSourcePlay(Source);
     
    	//Attendre la fin de la musique
    	ALint Status;
     
    	do
    	{
    		alGetSourcei(Source,AL_SOURCE_STATE, &Status);
    	}
    	while (Status == AL_PLAYING);
     
    	//Destruction du tampon
    	alDeleteBuffers(1, &buffer);
     
    	//Destruction de la source
    	alSourcei(Source, AL_BUFFER, 0);
    	alDeleteSources(1, &Source);
     
    	shutdownAL();
     
    } 
     
     
    void FenClient::donneesRecues()
    {
    	cout<<"recoi"<<endl;
    	QDataStream stream(socket);
     
    	if (tailleMessage == 0)
    	{
    		if(socket->bytesAvailable()<(int)sizeof(int))
    			return;
    		stream >> tailleMessage;
    	}
     
    	if (socket->bytesAvailable() < (tailleMessage)*sizeof(int))
    		return;
     
    	vector<ALshort> message;
    	for (int j =0 ; j<tailleMessage;j++)
    	{
    		int chiffre;
    		stream>>chiffre;
    		message.push_back(chiffre);
    	}
     
    	playSound(message);
    }
     
    QTcpSocket* FenClient::getSocket()
    {
      return socket;
    }
     
    void FenClient::donneesRecuesThread()
    {
        tEnvoie->start();
    }
     
    QMutex* FenClient::getMutex()
    {
      return mutex;
    }
     
    threadEnvoie::threadEnvoie(FenClient *fen)
    {
     
      fenClient=fen;
    }
     
     
    void threadEnvoie::run()
    {
      fenClient->getMutex()->lock();
      fenClient->donneesRecues();
      fenClient->getMutex()->unlock();
    }


    La derniere fonction utilise des mutex.

    Voila j'espere avoir ete assez clair en vous remerciant d'avance.

    Remarque lors du beug c'est comme si le thread ne finissait jamais et comme si le programme passait outre le lockage.

    EDIT : Finalement lorsque je met des mutex.lock et unlock aussi dans la fonction envoyerWave ca refonction bien que ca gele des le deuxieme appuie. Peut etre est -ce que la gestion des paquets est a revoir.

    EDIT : Peut etre que le serveur doit aussi etre en multithreading.

  2. #2
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Salut et bienvenue dans le monde du multithread
    Alors premier point qui ne va pas dans ton code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class threadEnvoie : public QThread
    {
     
     public:
      threadEnvoie(FenClient *fen);
      void run();
      FenClient* fenClient; <<-- ceci
    };
    Il faut que ta thread soit totalement indépendante de FenClient. Pour communiquer, utilise les signal/slot
    Dans ton cas tu ne devrais même pas avoir de mutex (pour l'instant). Au pire tu pourrais utiliser un sémaphore mais pour deux thread, à réfléchir.... http://qt.developpez.com/doc/4.4/threads-semaphores/

    Ton deuxième est ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    do
    	{
    		alGetSourcei(Source,AL_SOURCE_STATE, &Status);
    	}
    	while (Status == AL_PLAYING);
    tu block la thread principale pour envoyer tes buffers à openAL. Deux choix :
    1- tu ajout qApp->processEvents() dans la boucle: exécutera ce qui se passe dans l'event loop
    http://qt.developpez.com/doc/4.4/qco...#processevents
    2- tu découpe ta fonction playSound.

    je te conseil la deuxième qui te permettra de faire du streaming audio.

    Tu peu voir aussi les tutoriels et cours pour apprendre Qt : http://qt.developpez.com/tutoriels/

    PS: ici, tu te retrouve à faire du multithread surtout parce que tu bloque la thread principale. Tu devrais essayer de faire une version non multithread dans un premier temps .

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 42
    Points : 19
    Points
    19
    Par défaut
    Bonjour, tout d'abord merci de ton aide, mais il me reste quelques points à éclaircir :

    Ton deuxième est ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    do
    	{
    		alGetSourcei(Source,AL_SOURCE_STATE, &Status);
    	}
    	while (Status == AL_PLAYING);
    tu block la thread principale pour envoyer tes buffers à openAL. Deux choix :
    Il est pas censé se derouler dans la thread que j'ai créée ? Alors je vois pas pourquoi il bloque la thread principale.


    Puis lorsque tu parle de découper ma fonction playsound tu veux dire utiliser un systeme de remplissage de deux petits buffers l'un après l'autre ?
    Dans ce cas la thread principale sera quand même occuper la majorité du temps non ? pour pouvoir envoyer quelque chose au serveur va falloir tomber sur la miniseconde ou le buffer change ?

  4. #4
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par poulecaca Voir le message
    Il est pas censé se derouler dans la thread que j'ai créée ? Alors je vois pas pourquoi il bloque la thread principale.
    Vue comment tu as codé, il semble que oui elle s'execute dans la thread. Mais ce n'est pas logique!!!! Quand tu créé un objet il appartiens à une thread. Il donc préférable qu'il soit utilisé par cette thread et non une autre.

    Puis lorsque tu parle de découper ma fonction playsound tu veux dire utiliser un systeme de remplissage de deux petits buffers l'un après l'autre
    ?
    En gros c'est une architecture comme celle là que je te conseillerai :
    Un QReception qui reçois les samples audio.
    Un QEmettrice qui envoie des samples audio.
    Un QOpenAl (QObject manipulant OpenAl ,l'utiliser dans la thread de l'ihm me semble plus judicieux)
    La thread principale : IHM.

    Des que Qreception à reçu un certain nombre de données, elle peut l'envoyer à QOpenAl.

    QOpenAL vérifie à temps régulier l'état d'openAl.

    QEmettrice reçois régulièrement des samples qu'elle envoie.

    QEmettrice et Qreception pourrais être utilisé dans un thread.

    OpenAl utilisant déjà ses propres thread, il n'est pas très utile d'utiliser QOpenAL dans une thread à part

    A voire ce qui correspond et ne correspond pas à tes besoin. Tout dépend de ce que tu veut faire. Si tu ne veut pas de streaming, çà simplifie pas mal de chose.

    Dans ce cas la thread principale sera quand même occuper la majorité du temps non ? pour pouvoir envoyer quelque chose au serveur va falloir tomber sur la miniseconde ou le buffer change ?
    ??comment cela?


    tu n'as pas un problème ici :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    int chiffre;
    stream>>chiffre;
    tes samples sont du mono 16 bits mais tu li les données int par int qui sont plutôt en 32 ou 64 bits.

  5. #5
    Membre à l'essai
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 42
    Points : 19
    Points
    19
    Par défaut
    Ok ça commence à s'éclaircir.

    Donc en gros j'ai 3 nouveaux objets :

    QOpenAl : héritant de QObject permetant d'initialiser, lire et charger un sample.
    QReception : héritant de QThread qui envoie petit a petit le sample a lire a un QOpenAl
    QEmission : heritant lui aussi de QThread qui charge un sample petit a petit par le biais d'un autre QOpenAl et qui l'envoie au serveur.

    La FenClient orchestrant tout ca via des signal/slot.

    ]
    Dans ce cas la thread principale sera quand même occuper la majorité du temps non ? pour pouvoir envoyer quelque chose au serveur va falloir tomber sur la miniseconde ou le buffer change ?
    ??comment cela?
    Alors voila je pensais que QReception placerai ce qu'il recoit dans un buffer puis qu'il utiliserai : alSourceQueueBuffers. Pour lire a la suite de ce qui est en train d'etre lu.
    Mais voila ca va en faire beaucoup des buffers a créer. C'est pourquoi j'ai pensé créer 2 buffers qu'on remplit un par un, si un des buffers est vide on le remplit et on le met a la suite : alSourceQueueBuffers(Source, 1, Buffer[i]) (i=0 ou 1 suivant celui qui est vide). et on le lit.

    Si aucun des buffers sont vide on attend d'avoir un buffer lu entirement on le recupère (alSourceUnqueueBuffers(Source, 1, Buffer))et on le remplit et on le alSourceQueueBuffers(Source, 1, Buffer) et on le lit.
    Ceci dit si la thread attend d'avoir un buffer vide ca va pas la bloquer inutilement ?

    Voila est ce que je me trompe ?

    tu n'as pas un problème ici :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    int chiffre;
    stream>>chiffre;
    tes samples sont du mono 16 bits mais tu li les données int par int qui sont plutôt en 32 ou 64 bits.
    Mon stream contient des int lors de l'envoie je converti chaque ALshort en int.

  6. #6
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par poulecaca Voir le message
    Ok ça commence à s'éclaircir.

    Donc en gros j'ai 3 nouveaux objets :

    QOpenAl : héritant de QObject permetant d'initialiser, lire et charger un sample.
    QReception : héritant de QThread qui envoie petit a petit le sample a lire a un QOpenAl
    QEmission : heritant lui aussi de QThread qui charge un sample petit a petit par le biais d'un autre QOpenAl et qui l'envoie au serveur.

    La FenClient orchestrant tout ca via des signal/slot.
    en gors oui. QReception et QEmission seraient plutôt des QObject. Mais il peuvent être utilisé dans des thread.


    Alors voila je pensais que QReception placerai ce qu'il recoit dans un buffer puis qu'il utiliserai : alSourceQueueBuffers. Pour lire a la suite de ce qui est en train d'etre lu.
    Mais voila ca va en faire beaucoup des buffers a créer. C'est pourquoi j'ai pensé créer 2 buffers qu'on remplit un par un, si un des buffers est vide on le remplit et on le met a la suite : alSourceQueueBuffers(Source, 1, Buffer[i]) (i=0 ou 1 suivant celui qui est vide). et on le lit.

    Si aucun des buffers sont vide on attend d'avoir un buffer lu entirement on le recupère (alSourceUnqueueBuffers(Source, 1, Buffer))et on le remplit et on le alSourceQueueBuffers(Source, 1, Buffer) et on le lit
    Tu devrais trouver des exemple d'utilisation d'openAl avec des buffers (c'est la méthode que j'ai utilisé dans un projet). En gros tu utilise un QTimer qui à temps régulier va regarder le nombre de buffer OpenAl qui sont déjà lue et tu pourra ainsi les re-remplir. J'avais utilisé un truc comme 10 petit buffer OpenAl.
    http://loulou.developpez.com/tutorie.../premiers-pas/
    http://loulou.developpez.com/tutoriels/openal/flux-ogg/
    http://loulou.developpez.com/tutoriels/openal/capture/



    Citation Envoyé par poulecaca Voir le message
    Mon stream contient des int lors de l'envoie je converti chaque ALshort en int.
    ha ok, donc ça devrais aller. Par contre tu envoie deux fois trop (ou plus suivant la taille du int) de données du coup

  7. #7
    Membre à l'essai
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 42
    Points : 19
    Points
    19
    Par défaut
    Ok Ok merci beaucoup de ton aide.
    Bon je m'y met. Je vais essayer de faire tout ca.

    Pourquoi penses-tu qu'il serait que QEmission et QReception soient des QObject ?
    Avec cette méthode je pourrais envoyer un autre sample pendant que j'en lis un ?

  8. #8
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par poulecaca Voir le message
    Pourquoi penses-tu qu'il serait que QEmission et QReception soient des QObject ?
    C'est surtout parce que comme cela tu pourrais l'utiliser dans des thread ou non.
    Et que cela t'évitera pas mal de petit problème avec les threads car :
    1- un Qthread n'est pas une thread!!! seule la fonction run est la thread.
    2- ton objet utilisé par la thread devrais être créé dans le run
    3- dans ton cas tu as besoin d'une eventloop par thread

    Avec cette méthode je pourrais envoyer un autre sample pendant que j'en lis un ?
    la réception et l'émission sont threadés donc exécutés en parallèle donc normalement oui.

  9. #9
    Membre à l'essai
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 42
    Points : 19
    Points
    19
    Par défaut
    EDIT : autant pour moi une erreur dans la chronologie du code rien de grave .....
    je continue mon périple.

  10. #10
    Membre à l'essai
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 42
    Points : 19
    Points
    19
    Par défaut
    Il n'est pas possible d'initialiser la source d'openAl dans le constructeur de QOpenAl ?
    Ce que je veux dire par la c'est qu'en faisant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    QOpenAl::QOpenAl()
    {
      SampleRate=0;
      Source = new ALuint();
      alGenBuffers(2, Buffers);
      alGenSources(1, Source); <<<------ ici meme
    }
    Ma source est générée n'importe comment et inutilisable au moment de l'appel a la fonction playSoud censée lire tant que la source est en lecture.


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    void QOpenAl::playSound(vector<ALshort> musique)
    {
             if(!initOpenAl())
    	  {
    	    cout<<"impossible d'init"<<endl;
    		return;
    	  }
     
             //création tampon
    	ALuint buffer;
     
            //Pour l'instant getFreeBuffer() renvoie Buffers[0], j'en suis pas encore au streaming 
    	buffer=getFreeBuffer();
     
    	//Remplissage avec les echantillons lus
    	alBufferData(buffer,AL_FORMAT_MONO16, &musique[0],musique.size() * sizeof(ALshort),SampleRate);
     
    	if (alGetError() != AL_NO_ERROR)
    	  {
    	    cerr<<"OpenAl error"<<endl;
    	    return;
    	  }       
     
     
    	//On attache le tampon contenant les echantillons audio a la source
    	alSourcei(Source, AL_BUFFER, buffer);
     
    	  //Lecture
    	  alSourcePlay(*Source);
     
    	//Attendre la fin de la musique
     
    	do
    	{
    	  alGetSourcei(*Source,AL_SOURCE_STATE, &Status);
     
    	}
    	while (Status == AL_PLAYING); <<<<------- Comme la source est mal initialisée il termine tout de suite et on entend rien
     
    	shutdownAL();
    }
    Tout coup mon code se deroule normalement sauf que aucun son ne sors.
    En revanche si je genere ma source dans playSound ca marche sans probleme. Mais ca ne me va pas lorsque ej vais arriver au streaming vu que playSound sera executer plein de fois et donc ma source sera générée et detruite juste après.
    ps : peut etre je dois changer de topic ?

  11. #11
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968

  12. #12
    Membre à l'essai
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 42
    Points : 19
    Points
    19
    Par défaut
    Oui oui j'ai regardé le tuto mais eux ils utilisent la source sonore dans la fonction playSound moi j'ai besoin quelle soit initialisée avec l'objet QOpenAl

  13. #13
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    peut tu mettre le code de QOpenAl?

  14. #14
    Membre à l'essai
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 42
    Points : 19
    Points
    19
    Par défaut
    EDIt : Finalement j'utilise initOpenAl dasn le constructeur et shutdownAl dans le destructeur ca marche lors du premier passage dans playSound mais pas dans le deuxieme.
    EDIT :Ok ca marche maintenant je vais pouvoir m'interesser au streaming.


    Voila le .cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
     
    #include "FenClient.h"
     
    using namespace std;
     
     
    FenClient::FenClient()
    {
     
    	editAdresseIp = new QLineEdit("127.0.0.1");
    	log = new QTextEdit;
    	port = new QSpinBox();
    	port->setMaximum(65536);
    	port->setValue(12012);
     
    	boutonConnexion = new QPushButton("Connexion");
    	QPushButton *boutonQuitter = new QPushButton("Quitter");
    	test = new QPushButton("Test");
    	test->setEnabled(false);
     
    	QGridLayout *layout = new QGridLayout;
    	QGridLayout *layoutEdit = new QGridLayout;
    	layoutEdit->addWidget(log,0,0);
    	layout->setColumnStretch(2,8);
    	layout->setColumnStretch(0,1);
    	layout->setColumnStretch(4,1);
     
     
    	layout->addWidget(editAdresseIp,0,0,1,2);
    	layout->addWidget(port,0,4);
     
    	layout->addWidget(log,1,0,1,5);
    	layout->addWidget(boutonQuitter,2,0);
    	layout->addWidget(test,2,1);
    	layout->addWidget(boutonConnexion,2,4);
     
    	setLayout(layout);
    	socket = new QTcpSocket;
     
    	//tailleMessage = 0;
     
    	emission=new QEmission(socket);
    	reception=new QReception(socket);
     
    	connect(boutonQuitter,SIGNAL(clicked()),qApp,SLOT(quit()));
    	connect(boutonConnexion,SIGNAL(clicked()),this,SLOT(connexion()));
    	connect(socket,SIGNAL(connected()),this,SLOT(connected()));
    	connect(socket,SIGNAL(disconnected()),this,SLOT(deconnexion()));
    	connect(test,SIGNAL(clicked()),this,SLOT(envoiWave()));
    	connect(socket,SIGNAL(readyRead()), reception, SLOT(reception()));
    }
     
    FenClient::~FenClient()
    {
      delete emission;
      delete reception;
    }
     
    void FenClient::connexion()
    {
    	log->append(tr("<em>Tentative de connexion en cours ...</em>"));
    	socket->abort();
    	socket->connectToHost(editAdresseIp->text(), port->value());
    }
     
    void FenClient::connected()
    {
    	log->append(tr("<em>Connexion reussi vous pouvez parler</em>"));
    	boutonConnexion->setEnabled(false);
    	test->setEnabled(true);
    }
     
    void FenClient::deconnexion()
    {
      test->setEnabled(false);
      boutonConnexion->setEnabled(true);
      log->append(tr("<em>Deconnecté du serveur</em>"));
    }
     
    QOpenAl::QOpenAl()
    {
      SampleRate=0;
      Source = new ALuint();
      alGenBuffers(2, Buffers);
      alGenSources(1, Source);
      bufferAUtiliser=0;
    }
     
    QOpenAl::~QOpenAl()
    {
      //Destruction des buffers
      alDeleteBuffers(1, &Buffers[0]);
      alDeleteBuffers(1, &Buffers[1]);
      alSourcei(*Source, AL_BUFFER, 0);
      alDeleteSources(1, Source);
      delete Source;
     
    }
     
    bool QOpenAl::initOpenAl()
    {
     
    	//Ouverture du device
    	ALCdevice* Device = alcOpenDevice(NULL);
    	if(!Device)
    	{
    		cerr<<"impossible d'ouvrir le device"<<endl;
    	        return false;
    	 }
    	//Création contexte
    	ALCcontext *Context = alcCreateContext(Device, NULL);
    	if (!Context)
    	{
    		cerr<<"impossible d'initialiser un contexte"<<endl;
    		return false;
    	}
    	//Activationn du contexte
    	if (!alcMakeContextCurrent(Context))
    		return false;
     
    	return true;
    }
     
     
    vector<ALshort> QOpenAl::loadSound(const string& Filename)
    {
     
    	//librairy sndfile
    	//ouverture du fichier audio avec libsndfile
    	SF_INFO FileInfos;
    	SNDFILE* File = sf_open(Filename.c_str(), SFM_READ, &FileInfos);
    	if (!File)
    		cerr<<"impossible d'ouvrir le fichier"<<endl;
     
    	//Lecture du nombre d'echantillon et taux echantillon
    	ALsizei NbSamples = static_cast<ALsizei>(FileInfos.channels * FileInfos.frames);
    	SampleRate = static_cast<ALsizei>(FileInfos.samplerate);
     
    	//Lecture des echantillon audio au format 16 bits signe
    	vector<ALshort> Samples(NbSamples);
    	if (sf_read_short(File, &Samples[0], NbSamples)<NbSamples)
    		cerr<<"Impossible"<<endl;
     
    	//fermeture du fichier 
    	sf_close(File);
     
    	return Samples;
    }
     
    void FenClient::envoiWave()
    {
      emission->emissionWave();
      reception->getalPlay()->setSampleRate(emission->getalLoad()->getSampleRate());
    }
     
    void QOpenAl::shutdownAL()
    {
    	//Récupération du contexte et du device
    	ALCcontext* Context = alcGetCurrentContext();
    	ALCdevice* Device = alcGetContextsDevice(Context);
     
    	//Désactivation du contexte
    	alcMakeContextCurrent(NULL);
     
    	// Destruction du contexte
    	alcDestroyContext (Context);
     
    	//Fermeture du device
    	alcCloseDevice(Device);
    }
     
    void QOpenAl::setSampleRate(ALsizei _SampleRate)
    {
      SampleRate=_SampleRate;
    }  int tailleMessage;
     
    ALsizei QOpenAl::getSampleRate()
    {
      return SampleRate;
    }
     
    QOpenAl* QEmission::getalLoad()
    {
      return alLoad;
    }
     
    QEmission::QEmission(QTcpSocket *_socket)
    {
      socket=new QTcpSocket();
      socket=_socket;
      alLoad=new QOpenAl();
    }
     
    QEmission::~QEmission()
    {
      delete alLoad;
    }
     
    void QEmission::emissionWave()
    {
    	if(!alLoad->initOpenAl())
    	{
    		cerr<<"impossible d'envoyer le message"<<endl;
    		return;
    	}
     
    	vector<ALshort> sound;
    	sound=alLoad->loadSound("ocean.wav");
    	QByteArray paquet;
    	QDataStream stream(&paquet,QIODevice::WriteOnly);
    	stream<<static_cast <int> (sound.size());
     
    	for(int i =0;i<(int) sound.size();i++)
    	{
    		stream<<(int)sound[i];
    	}
     
    	socket->write(paquet);
     
    	alLoad->shutdownAL();
     
    }
     
     
    int QOpenAl::nbFreeBuffer()
    {
      ALint nbBuffer;
      alGetSourcei(*Source, AL_BUFFERS_PROCESSED, &nbBuffer);
      return (int)nbBuffer;
    }
     
     
    ALuint QOpenAl::getFreeBuffer()
    {
      ALuint buffer;
     
      /*if(nbFreeBuffer()>0)
      {
        cout<<"bing"<<endl;
        alSourceUnqueueBuffers(*Source, 1, &buffer);
      }
      else
      {*/
          buffer=Buffers[bufferAUtiliser];
          //bufferAUtiliser++;
          //}
     
      return buffer;
    }
     
    void QOpenAl::playSound(vector<ALshort> musique)
    {
      cout<<"play"<<endl;
    	if(!initOpenAl())
    	  {
    	    cout<<"impossible d'init"<<endl;
    		return;
    	  }
    	//alGenSources(1, Source);
     
    	//ALint Status;
    	//alGetSourcei(*Source,AL_SOURCE_STATE, &Status);
    	//while((nbFreeBuffer()<1)&&(Status==AL_PLAYING)) ;
    	//création tampon
    	ALuint buffer;
    	buffer=getFreeBuffer();
     
    	//Remplissage avec les echantillons lus
    	alBufferData(buffer,AL_FORMAT_MONO16, &musique[0],musique.size() * sizeof(ALshort),SampleRate);
     
    	if (alGetError() != AL_NO_ERROR)
    	  {
    	    cerr<<"OpenAl error"<<endl;
    	    return;
    	  }
     
     
     
    	//Création de la source
    	//ALuint Source;
    	//alGenSources(1, Source);
     
    	//On attache le tampon contenant les echantillons audio a la source
    	alSourceQueueBuffers(*Source, 1, &buffer);
     
    	//if(Status!=AL_PLAYING)
    	  //Lecture
    	  alSourcePlay(*Source);
     
    	//Attendre la fin de la musique
    	  ALint Status;
    	do
    	{
    	  alGetSourcei(*Source,AL_SOURCE_STATE, &Status);
     
    	}
    	while (Status == AL_PLAYING);
     
     
    	//Destruction du tampon
    	//alDeleteBuffers(1, &buffer);
     
    	//Destruction de la source
    	//alSourcei(*Source, AL_BUFFER, 0);
    	//alDeleteSources(1, Source);
     
    	shutdownAL();
     
    } 
     
     
    QReception::QReception(QTcpSocket *_socket)
    {
      socket=new QTcpSocket();
      socket=_socket;
      alPlay=new QOpenAl();
      tailleMessage=0;
    }
     
    QReception::~QReception()
    {
      delete alPlay;
    }
     
    QOpenAl* QReception::getalPlay()
    {
      return alPlay;
    }
     
    void QReception::reception()
    {
     
    	QDataStream stream(socket);
     
    	if (tailleMessage == 0)
    	{
    		if(socket->bytesAvailable()<(int)sizeof(int))
    			return;
    		stream >> tailleMessage;
    	}
     
    	if (socket->bytesAvailable() < (tailleMessage)*sizeof(int))
    		return;
     
    	vector<ALshort> message;
    	int k=0;
    	for (int j =0 ; j<tailleMessage;j++)
    	  {
    	    int chiffre;
    	    stream>>chiffre;
    	    message.push_back(chiffre);
    	    if(k>alPlay->getSampleRate())
    	      {
    		alPlay->playSound(message);
    		k=0;
    		message.clear();
    	      }
    	    k++;
    	  }	
     
    	alPlay->playSound(message);
    }
     
     
    QTcpSocket* FenClient::getSocket()
    {
      return socket;
    }
    et le .h :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    #ifndef HEADER_FENCLIENT
    #define HEADER_FENCLIENT
     
     
    #include <AL/al.h>
    #include <AL/alc.h>
    #include <sndfile.h>
    #include <QtGui>
    #include <QtNetwork>
    #include <iostream>
    #include <vector>
     
     
    class QOpenAl : public QObject
    {
     
     public:
     
      QOpenAl();
      ~QOpenAl();
     
      bool initOpenAl();
      std::vector<ALshort> loadSound(const std::string& Filename);
      void shutdownAL();
      void playSound(std::vector<ALshort> musique);
      void setSampleRate(ALsizei _SampleRate);
      ALsizei getSampleRate();
     
      ALuint getFreeBuffer();
      int nbFreeBuffer();
     
     private :
      ALuint Buffers[2];
      ALuint *Source;
      ALsizei SampleRate;
      int bufferAUtiliser;
     
    };
     
    class QReception : public QObject
    {
      Q_OBJECT
     
     public:
      QReception(QTcpSocket *_socket);
      ~QReception();
      QOpenAl *getalPlay();
      private slots:
      void reception();
     
     private:
      QOpenAl *alPlay;
      QTcpSocket *socket;
      int tailleMessage;
    };
     
    class QEmission : public QObject
    {
     
     public:
      QEmission(QTcpSocket *_socket);
      ~QEmission();
      QOpenAl *getalLoad();
      void emissionWave();
     
     private:
      QOpenAl *alLoad;
      QTcpSocket *socket;
    };
     
    class FenClient : public QWidget
    {
    	Q_OBJECT
     
    	public :
    	FenClient();
    	~FenClient();
    	//bool initOpenAl();
    	//std::vector<ALshort> loadSound(const std::string& Filename);
    	//void shutdownAL();
    	//void playSound(std::vector<ALshort> musique);
    	QTcpSocket* getSocket();
    	//void donneesRecues();
    	//QMutex* getMutex();
     
    	private slots:
    	void connexion();
    	void connected();
    	void deconnexion();
    	void envoiWave();
    	//void donneesRecuesThread();
     
    	private :
     
    	QTextEdit* log;
    	QLineEdit* editAdresseIp;
    	QSpinBox* port;
    	QPushButton *boutonConnexion;
    	QPushButton *test;
    	QEmission *emission;
    	QReception *reception;
    	QTcpSocket *socket;
    	//int tailleMessage;
    	//ALsizei SampleRate;
    };
     
     
    #endif


    Bon je suis desolé c'est pas très bien rangé encore mais c'est en chantier.

  15. #15
    Membre à l'essai
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 42
    Points : 19
    Points
    19
    Par défaut
    Un nouveau problem du au peut etre au QTimer est survenu mais j'ai delocalisé le sujet c'est plus du multithreading.
    Voila en tout cas merci de ton aide mongaulois.

  16. #16
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    résolu?
    je vais regarder ton autre topic

Discussions similaires

  1. Une action executée deux fois
    Par aelmalki dans le forum Struts 1
    Réponses: 1
    Dernier message: 31/07/2010, 21h22
  2. [MySQL] php/mysql requete s'executant deux fois
    Par soohikei dans le forum PHP & Base de données
    Réponses: 2
    Dernier message: 01/05/2010, 11h58
  3. [Sonar] Ne pas executer deux fois les tests
    Par woodwai dans le forum Qualimétrie
    Réponses: 3
    Dernier message: 23/03/2010, 17h54
  4. visual studio execute deux fois une page
    Par noknouka dans le forum Général Dotnet
    Réponses: 4
    Dernier message: 06/11/2009, 09h28
  5. Button OnClick s'execute deux fois de suite
    Par ToxiZz dans le forum ASP.NET
    Réponses: 2
    Dernier message: 06/06/2007, 08h59

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo