#ifndef _VOICE_H_
#define _VOICE_H_
//=============================================================================
//
// File : DccVoiceWindow.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-2010 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 option) 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 "DccVoiceCodec.h"
#include "DccDescriptor.h"
#include "DccThread.h"
#include "DccWindow.h"
#include "KviWindow.h"
#include "KviDataBuffer.h"
#include "kvi_sockettype.h"
#include "KviTalHBox.h"
#include "KviError.h"
#include <QLabel>
#include <QToolButton>
#include <QTimer>
class QSlider;
class DccMarshal;
#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
struct KviDccVoiceThreadOptions
{
bool bForceHalfDuplex;
int iPreBufferSize;
int iSampleRate;
KviCString szSoundDevice;
DccVoiceCodec * pCodec;
};
class DccVoiceThread : public DccThread
{
friend class DccVoiceWindow;
public:
DccVoiceThread(KviWindow * wnd, kvi_socket_t fd, KviDccVoiceThreadOptions * opt);
~DccVoiceThread();
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 DccVoiceWindow : public DccWindow
{
Q_OBJECT
public:
DccVoiceWindow(DccDescriptor * dcc, const char * name);
~DccVoiceWindow();
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;
DccVoiceThread * m_pSlaveThread;
protected:
void focusInEvent(QFocusEvent *) override;
const QString & target() override;
void fillCaptionBuffers() override;
QPixmap * myIconPtr() override;
void resizeEvent(QResizeEvent * e) override;
QSize sizeHint() const override;
bool event(QEvent * e) override;
void getBaseLogFileName(QString & buffer) override;
void startTalking();
void stopTalking();
void startConnection();
int getMixerVolume(void) const;
protected slots:
void handleMarshalError(KviError::Code eError);
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 = nullptr;
}
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 = nullptr;
m_pAudioDecoder = nullptr;
}
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 completely 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_