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
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
|
#ifndef _VOICE_H_
#define _VOICE_H_
//
// File : voice.h
// Creation date : Thu Aug 23 04:08:10 2001 GMT by Szymon Stefanek
//
// This file is part of the KVirc irc client distribution
// Copyright (C) 2001 Szymon Stefanek (pragma at kvirc dot net)
//
// This program is FREE software. You can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your opinion) any later version.
//
// This program is distributed in the HOPE that it will be USEFUL,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, write to the Free Software Foundation,
// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "kvi_window.h"
#include "kvi_databuffer.h"
#include "kvi_sockettype.h"
#include "codec.h"
#include "descriptor.h"
#include "thread.h"
#include "window.h"
#include "kvi_tal_hbox.h"
#include <qlabel.h>
#include <qtoolbutton.h>
#include <qtimer.h>
#ifndef _DCC_VOICE_CPP_
extern bool kvi_dcc_voice_is_valid_codec(const char * codecName);
#endif
#define KVI_DCC_VOICE_THREAD_ACTION_START_RECORDING 0
#define KVI_DCC_VOICE_THREAD_ACTION_STOP_RECORDING 1
#define KVI_DCC_VOICE_THREAD_ACTION_START_PLAYING 2
#define KVI_DCC_VOICE_THREAD_ACTION_STOP_PLAYING 3
typedef struct _KviDccVoiceThreadOptions
{
bool bForceHalfDuplex;
int iPreBufferSize;
int iSampleRate;
KviStr szSoundDevice;
KviDccVoiceCodec * pCodec;
} KviDccVoiceThreadOptions;
class KviDccVoiceThread : public KviDccThread
{
friend class KviDccVoice;
public:
KviDccVoiceThread(KviWindow * wnd,kvi_socket_t fd,KviDccVoiceThreadOptions * opt);
~KviDccVoiceThread();
protected:
// bool m_bUseGsm;
KviDccVoiceThreadOptions * m_pOpt;
int m_soundFd;
int m_soundFdMode;
KviDataBuffer m_outFrameBuffer;
KviDataBuffer m_inFrameBuffer;
KviDataBuffer m_inSignalBuffer;
KviDataBuffer m_outSignalBuffer;
bool m_bPlaying;
bool m_bRecording;
bool m_bRecordingRequestPending;
bool m_bSoundcardChecked;
int m_iLastSignalBufferSize;
long m_iLastSignalBufferTime;
// unsigned int m_uSleepTime;
KviMutex * m_pInfoMutex;
// stuff protected by the mutex:
int m_iInputBufferSize;
int m_iOutputBufferSize;
protected:
bool checkSoundcard();
bool openSoundcardWithDuplexOption(int openMode,int failMode);
bool openSoundcard(int mode);
bool openSoundcardForWriting();
bool openSoundcardForReading();
void closeSoundcard();
bool readWriteStep();
bool soundStep();
void startRecording();
void stopRecording();
void startPlaying();
void stopPlaying();
virtual void run();
};
class KviDccMarshal;
class QSlider;
class KviDccVoice : public KviDccWindow
{
Q_OBJECT
public:
KviDccVoice(KviFrame *pFrm,KviDccDescriptor * dcc,const char * name);
~KviDccVoice();
protected:
KviTalHBox * m_pHBox;
QSlider * m_pVolumeSlider;
QLabel * m_pInputLabel;
QLabel * m_pOutputLabel;
QLabel * m_pRecordingLabel;
QLabel * m_pPlayingLabel;
QToolButton * m_pTalkButton;
QTimer * m_pUpdateTimer;
QString m_szTarget;
KviDccVoiceThread * m_pSlaveThread;
protected:
virtual void focusInEvent(QFocusEvent *);
virtual const QString & target();
virtual void fillCaptionBuffers();
virtual QPixmap * myIconPtr();
virtual void resizeEvent(QResizeEvent *e);
virtual QSize sizeHint() const;
virtual bool event(QEvent *e);
virtual void getBaseLogFileName(KviStr &buffer);
void startTalking();
void stopTalking();
void startConnection();
int getMixerVolume(void) const;
protected slots:
void handleMarshalError(int err);
void connected();
void updateInfo();
void startOrStopTalking(bool bStart);
void setMixerVolume(int);
void connectionInProgress();
// void stopTalking();
};
#if 0
/*
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
CODEC DEFINITION
Sample rate = samples/sec (ex. 8000)
Sample size = bits (ex. 16 bits)
Sample endianness = le/be
Sample compressor = name
<rate>:<bits>:<endianness>:<compressor>
8000:16:le:null
8000:16:le:gsm
8000:16:le:adpcm
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
class KviVoiceParty
{
public:
KviVoiceParty(const QString &szNick,const QString &szIp,unsigned short uPort);
~KviVoiceParty();
protected:
QString m_szIp;
unsigned short m_uPort;
QString m_szNick;
KviPointerList<KviVoiceParty> * m_pChildrenTree;
public:
const QString & ip(){ return m_szIp; };
unsigned short port(){ return m_uPort; };
const QString & nick(){ return m_szNick; };
void addChild(KviVoiceParty * pChild);
};
KviVoiceParty::KviVoiceParty(const QString &szNick,const QString &szIp,unsigned short uPort)
: m_szIp(szIp), m_uPort(uPort), m_szNick(szNick)
{
m_pChildrenTree = 0;
}
KviVoiceParty::~KviVoiceParty()
{
if(m_pChildrenTree)delete m_pChildrenTree;
}
void KviVoiceParty::addChild(KviVoiceParty * pChild)
{
if(!m_pChildrenTree)
{
m_pChildrenTree = new KviPointerList<KviVoiceParty>;
m_pChildrenTree->setAutoDelete(true);
}
m_pChildrenTree->append(pChild);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
class KviVoiceAudioEncoder
{
public:
KviVoiceAudioEncoder();
~KviVoiceAudioEncoder();
public:
};
class KviVoiceAudioDecoder
{
public:
KviVoiceAudioDecoder();
~KviVoiceAudioDecoder();
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
class KviVoiceLink
{
public:
KviVoiceLink(KviVoiceParty * pRemoteParty);
~KviVoiceLink();
protected:
QString m_szId;
KviVoiceParty * m_pRemoteParty;
KviVoiceAudioEncoder * m_pAudioEncoder;
KviVoiceAudioDecoder * m_pAudioDecoder;
public:
const QStirng & id(){ return m_szId; };
KviVoiceParty * remoteParty(){ return m_pRemoteParty; };
};
KviVoiceLink::KviVoiceLink(KviVoiceParty * pRemoteParty)
{
KviQString::sprintf("%Q:%u",&(pRemoteParty->nick()),pRemoteParty->port());
m_pRemoteParty = pRemoteParty;
m_pAudioEncoder = 0;
m_pAudioDecoder = 0;
}
KviVoiceLink::~KviVoiceLink()
{
delete m_pRemoteParty;
if(m_pAudioEncoder)delete m_pAudioEncoder;
if(m_pAudioDecoder)delete m_pAudioDecoder;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
class KviVoice_r8000s16eL_to_r8000s16eB_Transformer
{
}
class KviVoice_r8000s16eB_to_r8000s16eL_Transformer
{
}
class KviVoice_r11025s16eL_to_r11025s16eB_Transformer
{
}
class KviVoice_r11025s16eB_to_r11025s16eL_Transformer
{
}
class KviVoice_r11025s16eL_to_r8000s16eL_Transformer
{
}
class KviVoice_r11025s16eL_to_r8000s16eB_Transformer
{
}
class KviVoice_r11025s16eB_to_r8000s16eL_Transformer
{
}
class KviVoice_r8000s16eL_to_r11025s16eL_Transformer
{
}
class KviVoice_r8000s16eL_to_r11025s16eB_Transformer
{
}
class KviVoice_r8000s16eB_to_r11025s16eL_Transformer
{
}
class KviVoiceConference
{
public:
KviVoiceConference();
~KviVoiceConference();
public:
KviPointerList<KviVoiceLink> * m_pLinks;
KviPointerHashTable<QString,KviVoiceLink> *
SOCKET m_hUdpSocket;
QString m_szLastError;
unsigned int m_uLocalAudioSampleRate; // samples/sec
unsigned int m_uLocalAudioSampleSize; // bits
unsigned int m_uLocalAudioEndianness; // 0 = le, 1 = be
public:
void conferenceThread();
protected:
void conferenceThreadMain();
bool setupUdpSocket();
};
KviVoiceConference::KviVoiceConference()
{
m_pLinks = new KviPointerList<KviVoiceLink>;
m_pLinks->setAutoDelete(true);
}
KviVoiceConference::~KviVoiceConference()
{
delete m_pLinks;
}
bool KviVoiceConference::setupUdpSocket()
{
return true;
}
void KviVoiceConference::shutdownUdpSocket()
{
}
void KviVoiceConference::conferenceThreadMain()
{
for(;;)
{
readAndDecompressIncomingDataForEveryLink();
mixIncomingDataToASingleStream();
playIncomingDataSingleStream();
readLocalAudioStream();
foreach(link)
{
mixLocalAndOtherIncomingDataStreams()
compressAndSendOtherIncomingDataStreams()
}
}
}
void KviVoiceConference::conferenceThread()
{
if(!setupUdpSocket())return;
conferenceThreadMain();
shutdownUdpSocket();
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
*/
// DCC VOICE NG proto
//
// tcp control connection
// --> HELLO: DccVoice protocol header
// <-- HELLO: DccVoice protocol header
// --> IACCEPT: Codec-description|CodecId,Codec-description|CodecID,Codec... (in order of preference)
// <-- IACCEPT: Codec-description|CodecID,Codec-description|CodecId,Codec... (in order of preference)
// --> MYADDRESS
// <-- MYADDRESS
// --> YOURIDIS: <local id for the remote end> (CID)
// <-- YOURIDIS: <local id for the remote end> (CID)
// Audio is sent in blocks broken in chunks broken in udp packets
// Each block is a set of consecutive audio chunks that theoretically
// should be played consecutively.
// Each packet in a chunk has an ordinal
// Chunks must be relatively short in order
// to allow a remote end that looses a packet to
// synchronize after a short period of time
// The maximum number of packets in a chunk is 65535 (but a chunk should be no more than 24-32 KBytes in size
// and in general they should be as small as possible, even one packet per chunk, if the codec allows it)
// Each chunk should be encoded independently of the others
// We can switch codec at each chunk (but not at each packet)
// When some packets are lost we loose the entire chunk
// A block is completly synchronized in time (unless we loose some chunks: in that case
// we may decide to synchronize with silence or insert a glitch...)
// Decoding never depends on the future
// Each packet should be decompressable (eventually dependently on the previous in the chunk)
// but playable independently
// start UDP stream
// UDP Packet format:
// <magic byte>: byte
// <magic byte>: byte
// <local id>: word
// <payload len>: word
// <payload>
// Payload format:
// <codec id>: word
// <ordinal in a chunk>: word (0 = beginning of a chunk)
// read raw audio data at sample rate X, sample size Y
// multiplex data always at this sample rate and sample size
#endif
#endif //_VOICE_H_
|