#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 #include #include 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 ::: 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 * 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; 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 * m_pLinks; KviPointerHashTable * 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; 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: (CID) // <-- YOURIDIS: (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: // : byte // : byte // : word // : word // // Payload format: // : word // : 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_