From 54462f0276401bfe9272fb6bad1d0e983952561b Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 13 Nov 2014 12:18:04 +0100 Subject: [PATCH 1/2] Prepare Core for av groupchats --- src/core.cpp | 18 ++++++++--- src/core.h | 4 +++ src/coreav.cpp | 84 +++++++++++++++++++++++++++++++++++++++++++++----- src/coreav.h | 12 +++++++- 4 files changed, 106 insertions(+), 12 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index b23822668..8f3ecb38a 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -45,6 +45,7 @@ const QString Core::CONFIG_FILE_NAME = "data"; const QString Core::TOX_EXT = ".tox"; QList Core::fileSendQueue; QList Core::fileRecvQueue; +QHash Core::groupCalls; Core::Core(Camera* cam, QThread *coreThread, QString loadPath) : tox(nullptr), camera(cam), loadPath(loadPath), ready{false} @@ -95,12 +96,15 @@ Core::Core(Camera* cam, QThread *coreThread, QString loadPath) : } QString inDevDescr = Settings::getInstance().getInDev(); + int stereoFlag = av_DefaultSettings.audio_channels==1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; if (inDevDescr.isEmpty()) - alInDev = alcCaptureOpenDevice(nullptr,av_DefaultSettings.audio_sample_rate, AL_FORMAT_MONO16, - (av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4) / 1000); + alInDev = alcCaptureOpenDevice(nullptr,av_DefaultSettings.audio_sample_rate, stereoFlag, + (av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4) + / 1000 * av_DefaultSettings.audio_channels); else - alInDev = alcCaptureOpenDevice(inDevDescr.toStdString().c_str(),av_DefaultSettings.audio_sample_rate, AL_FORMAT_MONO16, - (av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4) / 1000); + alInDev = alcCaptureOpenDevice(inDevDescr.toStdString().c_str(),av_DefaultSettings.audio_sample_rate, stereoFlag, + (av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4) + / 1000 * av_DefaultSettings.audio_channels); if (!alInDev) qWarning() << "Core: Cannot open input audio device"; } @@ -1632,11 +1636,17 @@ void Core::groupInviteFriend(int friendId, int groupId) void Core::createGroup(uint8_t type) { if (type == TOX_GROUPCHAT_TYPE_TEXT) + { emit emptyGroupCreated(tox_add_groupchat(tox)); + } else if (type == TOX_GROUPCHAT_TYPE_AV) + { emit emptyGroupCreated(toxav_add_av_groupchat(tox, playGroupAudio, this)); + } else + { qWarning() << "Core::createGroup: Unknown type "< fileSendQueue, fileRecvQueue; static ToxCall calls[]; + static QHash groupCalls; // Maps group IDs to ToxGroupCalls QMutex fileSendMutex, messageSendMutex; bool ready; diff --git a/src/coreav.cpp b/src/coreav.cpp index cf036dee9..62fa09a23 100644 --- a/src/coreav.cpp +++ b/src/coreav.cpp @@ -176,7 +176,6 @@ void Core::startCall(int friendId, bool video) emit avCallFailed(friendId); return; } - } } @@ -222,7 +221,7 @@ void Core::sendCallAudio(int callId, ToxAv* toxav) return; } - int framesize = (calls[callId].codecSettings.audio_frame_duration * calls[callId].codecSettings.audio_sample_rate) / 1000; + int framesize = (calls[callId].codecSettings.audio_frame_duration * calls[callId].codecSettings.audio_sample_rate) / 1000 * av_DefaultSettings.audio_channels; uint8_t buf[framesize*2], dest[framesize*2]; bool frame = false; @@ -578,7 +577,7 @@ void Core::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, un if(state != AL_PLAYING) { alSourcePlay(alSource); - qDebug() << "Core: Starting audio source " << (int)alSource; + //qDebug() << "Core: Starting audio source " << (int)alSource; } } @@ -587,10 +586,81 @@ VideoSource *Core::getVideoSourceFromCall(int callNumber) return &calls[callNumber].videoSource; } -void Core::playGroupAudio(Tox* /*tox*/, int /*groupnumber*/, int /*friendgroupnumber*/, const int16_t* out_audio, +void Core::playGroupAudio(Tox* /*tox*/, int groupnumber, int /*friendgroupnumber*/, const int16_t* out_audio, unsigned out_audio_samples, uint8_t decoder_channels, unsigned audio_sample_rate, void* /*userdata*/) { - /// TODO: FIXME: Don't play groupchat audio on the main source! - /// We'll need some sort of call[] array but for groupchats, when that's done use this source - playAudioBuffer(alMainSource, out_audio, out_audio_samples, decoder_channels, audio_sample_rate); + if (!groupCalls[groupnumber].active) + return; + + playAudioBuffer(groupCalls[groupnumber].alSource, out_audio, out_audio_samples, decoder_channels, audio_sample_rate); +} + +void Core::joinGroupCall(int groupId, ToxAv *toxav) +{ + qDebug() << QString("Core: Joining group call %1").arg(groupId); + groupCalls[groupId].groupId = groupId; + groupCalls[groupId].muteMic = false; + groupCalls[groupId].muteVol = false; + // the following three lines are also now redundant from startCall, but are + // necessary there for outbound and here for inbound + groupCalls[groupId].codecSettings = av_DefaultSettings; + groupCalls[groupId].codecSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH; + groupCalls[groupId].codecSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT; + + // Audio + alGenSources(1, &calls[groupId].alSource); + alcCaptureStart(alInDev); + + // Go + groupCalls[groupId].active = true; + groupCalls[groupId].sendAudioTimer->setInterval(5); + groupCalls[groupId].sendAudioTimer->setSingleShot(true); + connect(groupCalls[groupId].sendAudioTimer, &QTimer::timeout, [=](){sendGroupCallAudio(groupId,toxav);}); + groupCalls[groupId].sendAudioTimer->start(); +} + +void Core::leaveGroupCall(int groupId, ToxAv *) +{ + qDebug() << QString("Core: Leaving group call %1").arg(groupId); + groupCalls[groupId].active = false; + disconnect(groupCalls[groupId].sendAudioTimer,0,0,0); + groupCalls[groupId].sendAudioTimer->stop(); + alcCaptureStop(alInDev); +} + +void Core::sendGroupCallAudio(int groupId, ToxAv* toxav) +{ + if (!groupCalls[groupId].active) + return; + + if (groupCalls[groupId].muteMic) + { + groupCalls[groupId].sendAudioTimer->start(); + return; + } + + int framesize = (groupCalls[groupId].codecSettings.audio_frame_duration * groupCalls[groupId].codecSettings.audio_sample_rate) / 1000 * av_DefaultSettings.audio_channels; + uint8_t buf[framesize*2]; + + bool frame = false; + ALint samples; + alcGetIntegerv(alInDev, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples); + if(samples >= framesize) + { + alcCaptureSamples(alInDev, buf, framesize); + frame = 1; + } + + if(frame) + { + int r; + if((r = toxav_group_send_audio(toxav_get_tox(toxav), groupId, (int16_t*)buf, + framesize, av_DefaultSettings.audio_channels, av_DefaultSettings.audio_sample_rate)) < 0) + { + qDebug() << "Core: toxav_group_send_audio error"; + groupCalls[groupId].sendAudioTimer->start(); + return; + } + } + groupCalls[groupId].sendAudioTimer->start(); } diff --git a/src/coreav.h b/src/coreav.h index bbb2a62f8..4619796d7 100644 --- a/src/coreav.h +++ b/src/coreav.h @@ -16,7 +16,6 @@ class QTimer; struct ToxCall { -public: ToxAvCSettings codecSettings; QTimer *sendAudioTimer, *sendVideoTimer; int callId; @@ -29,4 +28,15 @@ public: NetVideoSource videoSource; }; +struct ToxGroupCall +{ + ToxAvCSettings codecSettings; + QTimer *sendAudioTimer; + int groupId; + bool active; + bool muteMic; + bool muteVol; + ALuint alSource; +}; + #endif // COREAV_H From eac712e68a40749c3ddd4d79f7a3fc6a0974f23e Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 13 Nov 2014 13:11:23 +0100 Subject: [PATCH 2/2] Add GUI for audio groupchats --- src/core.cpp | 5 +- src/core.h | 11 +++- src/coreav.cpp | 34 ++++++++++-- src/group.cpp | 4 +- src/group.h | 3 +- src/grouplist.cpp | 4 +- src/grouplist.h | 2 +- src/widget/form/chatform.cpp | 2 - src/widget/form/chatform.h | 2 - src/widget/form/genericchatform.cpp | 2 + src/widget/form/genericchatform.h | 2 + src/widget/form/groupchatform.cpp | 84 +++++++++++++++++++++++++++-- src/widget/form/groupchatform.h | 4 ++ src/widget/widget.cpp | 2 +- 14 files changed, 138 insertions(+), 23 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index 8f3ecb38a..4a2ad0279 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -46,12 +46,15 @@ const QString Core::TOX_EXT = ".tox"; QList Core::fileSendQueue; QList Core::fileRecvQueue; QHash Core::groupCalls; +QThread* Core::coreThread{nullptr}; -Core::Core(Camera* cam, QThread *coreThread, QString loadPath) : +Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) : tox(nullptr), camera(cam), loadPath(loadPath), ready{false} { qDebug() << "Core: loading Tox from" << loadPath; + coreThread = CoreThread; + videobuf = new uint8_t[videobufsize]; for (int i = 0; i < ptCounter; i++) diff --git a/src/core.h b/src/core.h index e0b9a23d2..2bdb97c4d 100644 --- a/src/core.h +++ b/src/core.h @@ -117,6 +117,13 @@ public slots: void micMuteToggle(int callId); void volMuteToggle(int callId); + static void joinGroupCall(int groupId); ///< Starts a call in an existing AV groupchat + static void leaveGroupCall(int groupId); ///< Will not leave the group, just stop the call + static void disableGroupCallMic(int groupId); + static void disableGroupCallVol(int groupId); + static void enableGroupCallMic(int groupId); + static void enableGroupCallVol(int groupId); + void setPassword(QString& password, PasswordType passtype, uint8_t* salt = nullptr); void clearPassword(PasswordType passtype); QByteArray encryptData(const QByteArray& data, PasswordType passtype); @@ -233,8 +240,6 @@ private: static void onAvPeerTimeout(void* toxav, int32_t call_index, void* core); static void onAvMediaChange(void *toxav, int32_t call_index, void* core); - static void joinGroupCall(int groupId, ToxAv *toxav); ///< Starts a call in an existing AV groupchat - static void leaveGroupCall(int groupId, ToxAv *toxav); ///< Will not leave the group, just stop the call static void playGroupAudio(Tox* tox, int groupnumber, int friendgroupnumber, const int16_t* out_audio, unsigned out_audio_samples, uint8_t decoder_channels, unsigned audio_sample_rate, void* userdata); static void sendGroupCallAudio(int groupId, ToxAv* toxav); @@ -282,6 +287,8 @@ private: static ALCdevice* alOutDev, *alInDev; static ALCcontext* alContext; + + static QThread *coreThread; public: static ALuint alMainSource; }; diff --git a/src/coreav.cpp b/src/coreav.cpp index 62fa09a23..08824f530 100644 --- a/src/coreav.cpp +++ b/src/coreav.cpp @@ -592,10 +592,13 @@ void Core::playGroupAudio(Tox* /*tox*/, int groupnumber, int /*friendgroupnumbe if (!groupCalls[groupnumber].active) return; - playAudioBuffer(groupCalls[groupnumber].alSource, out_audio, out_audio_samples, decoder_channels, audio_sample_rate); + if (!groupCalls[groupbumber].muteVol) + return; + + playAudioBuffer(alMainSource, out_audio, out_audio_samples, decoder_channels, audio_sample_rate); } -void Core::joinGroupCall(int groupId, ToxAv *toxav) +void Core::joinGroupCall(int groupId) { qDebug() << QString("Core: Joining group call %1").arg(groupId); groupCalls[groupId].groupId = groupId; @@ -608,10 +611,13 @@ void Core::joinGroupCall(int groupId, ToxAv *toxav) groupCalls[groupId].codecSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT; // Audio - alGenSources(1, &calls[groupId].alSource); + alGenSources(1, &groupCalls[groupId].alSource); alcCaptureStart(alInDev); // Go + ToxAv* toxav = Core::getInstance()->toxav; + groupCalls[groupId].sendAudioTimer = new QTimer(); + groupCalls[groupId].sendAudioTimer->moveToThread(coreThread); groupCalls[groupId].active = true; groupCalls[groupId].sendAudioTimer->setInterval(5); groupCalls[groupId].sendAudioTimer->setSingleShot(true); @@ -619,7 +625,7 @@ void Core::joinGroupCall(int groupId, ToxAv *toxav) groupCalls[groupId].sendAudioTimer->start(); } -void Core::leaveGroupCall(int groupId, ToxAv *) +void Core::leaveGroupCall(int groupId) { qDebug() << QString("Core: Leaving group call %1").arg(groupId); groupCalls[groupId].active = false; @@ -664,3 +670,23 @@ void Core::sendGroupCallAudio(int groupId, ToxAv* toxav) } groupCalls[groupId].sendAudioTimer->start(); } + +void Core::disableGroupCallMic(int groupId) +{ + groupCalls[groupId].muteMic = true; +} + +void Core::disableGroupCallVol(int groupId) +{ + groupCalls[groupId].muteVol = true; +} + +void Core::enableGroupCallMic(int groupId) +{ + groupCalls[groupId].muteMic = false; +} + +void Core::enableGroupCallVol(int groupId) +{ + groupCalls[groupId].muteVol = false; +} diff --git a/src/group.cpp b/src/group.cpp index d74610131..37160fef0 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -23,8 +23,8 @@ #include #include -Group::Group(int GroupId, QString Name) - : groupId(GroupId), nPeers{0} +Group::Group(int GroupId, QString Name, bool IsAvGroupchat) + : groupId(GroupId), nPeers{0}, avGroupchat{IsAvGroupchat} { widget = new GroupWidget(groupId, Name); chatForm = new GroupChatForm(this); diff --git a/src/group.h b/src/group.h index cd263538f..56c3bebb0 100644 --- a/src/group.h +++ b/src/group.h @@ -30,7 +30,7 @@ class Group : public QObject { Q_OBJECT public: - Group(int GroupId, QString Name); + Group(int GroupId, QString Name, bool IsAvGroupchat); ~Group(); void addPeer(int peerId, QString name); void removePeer(int peerId); @@ -43,6 +43,7 @@ public: GroupWidget* widget; GroupChatForm* chatForm; int hasNewMessages, userWasMentioned; + bool avGroupchat; }; #endif // GROUP_H diff --git a/src/grouplist.cpp b/src/grouplist.cpp index 10163adfa..72b7cf872 100644 --- a/src/grouplist.cpp +++ b/src/grouplist.cpp @@ -19,9 +19,9 @@ QList GroupList::groupList; -Group* GroupList::addGroup(int groupId, const QString& name) +Group* GroupList::addGroup(int groupId, const QString& name, bool isAvGroupchat) { - Group* newGroup = new Group(groupId, name); + Group* newGroup = new Group(groupId, name, isAvGroupchat); groupList.append(newGroup); return newGroup; } diff --git a/src/grouplist.h b/src/grouplist.h index 4e2b3e35b..36d8281f7 100644 --- a/src/grouplist.h +++ b/src/grouplist.h @@ -26,7 +26,7 @@ class GroupList { public: GroupList(); - static Group* addGroup(int groupId, const QString& name); + static Group* addGroup(int groupId, const QString& name, bool isAvGroupchat); static Group* findGroup(int groupId); static void removeGroup(int groupId, bool fake = false); diff --git a/src/widget/form/chatform.cpp b/src/widget/form/chatform.cpp index 7e52136ed..ac46e2340 100644 --- a/src/widget/form/chatform.cpp +++ b/src/widget/form/chatform.cpp @@ -43,8 +43,6 @@ ChatForm::ChatForm(Friend* chatFriend) : f(chatFriend) - , audioInputFlag(false) - , audioOutputFlag(false) , callId(0) { nameLabel->setText(f->getDisplayedName()); diff --git a/src/widget/form/chatform.h b/src/widget/form/chatform.h index 2a051f84e..4ec1735d7 100644 --- a/src/widget/form/chatform.h +++ b/src/widget/form/chatform.h @@ -95,8 +95,6 @@ private: Friend* f; CroppingLabel *statusMessageLabel; NetCamView* netcam; - bool audioInputFlag; - bool audioOutputFlag; int callId; QLabel *callDuration; QTimer *timer; diff --git a/src/widget/form/genericchatform.cpp b/src/widget/form/genericchatform.cpp index ffe6f8ecb..71dc14771 100644 --- a/src/widget/form/genericchatform.cpp +++ b/src/widget/form/genericchatform.cpp @@ -37,6 +37,8 @@ GenericChatForm::GenericChatForm(QWidget *parent) : QWidget(parent), earliestMessage(nullptr) + , audioInputFlag(false) + , audioOutputFlag(false) { curRow = 0; diff --git a/src/widget/form/genericchatform.h b/src/widget/form/genericchatform.h index ddd451ca7..47128a0f3 100644 --- a/src/widget/form/genericchatform.h +++ b/src/widget/form/genericchatform.h @@ -91,6 +91,8 @@ protected: QPushButton *sendButton; ChatAreaWidget *chatWidget; QDateTime *earliestMessage; + bool audioInputFlag; + bool audioOutputFlag; }; #endif // GENERICCHATFORM_H diff --git a/src/widget/form/groupchatform.cpp b/src/widget/form/groupchatform.cpp index 9666b7e4f..fb8a4faef 100644 --- a/src/widget/form/groupchatform.cpp +++ b/src/widget/form/groupchatform.cpp @@ -30,17 +30,25 @@ #include "src/misc/flowlayout.h" GroupChatForm::GroupChatForm(Group* chatGroup) - : group(chatGroup) + : group(chatGroup), inCall{false} { nusersLabel = new QLabel(); tabber = new TabCompleter(msgEdit, group); fileButton->setEnabled(false); - callButton->setVisible(false); - videoButton->setVisible(false); - volButton->setVisible(false); - micButton->setVisible(false); + if (group->avGroupchat) + { + videoButton->setEnabled(false); + videoButton->setObjectName("grey"); + } + else + { + videoButton->setVisible(false); + callButton->setVisible(false); + volButton->setVisible(false); + micButton->setVisible(false); + } nameLabel->setText(group->widget->getName()); @@ -68,6 +76,9 @@ GroupChatForm::GroupChatForm(Group* chatGroup) connect(msgEdit, SIGNAL(enterPressed()), this, SLOT(onSendTriggered())); connect(msgEdit, &ChatTextEdit::tabPressed, tabber, &TabCompleter::complete); connect(msgEdit, &ChatTextEdit::keyPressed, tabber, &TabCompleter::reset); + connect(callButton, &QPushButton::clicked, this, &GroupChatForm::onCallClicked); + connect(micButton, SIGNAL(clicked()), this, SLOT(onMicMuteToggle())); + connect(volButton, SIGNAL(clicked()), this, SLOT(onVolMuteToggle())); setAcceptDrops(true); } @@ -129,3 +140,66 @@ void GroupChatForm::dropEvent(QDropEvent *ev) } } +void GroupChatForm::onMicMuteToggle() +{ + if (audioInputFlag == true) + { + if (micButton->objectName() == "red") + { + Core::getInstance()->enableGroupCallMic(group->groupId); + micButton->setObjectName("green"); + } + else + { + Core::getInstance()->disableGroupCallMic(group->groupId); + micButton->setObjectName("red"); + } + + Style::repolish(micButton); + } +} + +void GroupChatForm::onVolMuteToggle() +{ + if (audioOutputFlag == true) + { + if (volButton->objectName() == "red") + { + Core::getInstance()->enableGroupCallVol(group->groupId); + volButton->setObjectName("green"); + } + else + { + Core::getInstance()->disableGroupCallVol(group->groupId); + volButton->setObjectName("red"); + } + + Style::repolish(volButton); + } +} + +void GroupChatForm::onCallClicked() +{ + if (!inCall) + { + Core::getInstance()->joinGroupCall(group->groupId); + audioInputFlag = true; + audioOutputFlag = true; + callButton->setObjectName("red"); + callButton->style()->polish(callButton); + inCall = true; + } + else + { + Core::getInstance()->leaveGroupCall(group->groupId); + audioInputFlag = false; + audioOutputFlag = false; + micButton->setObjectName("green"); + micButton->style()->polish(micButton); + volButton->setObjectName("green"); + volButton->style()->polish(volButton); + callButton->setObjectName("green"); + callButton->style()->polish(callButton); + inCall = false; + } +} diff --git a/src/widget/form/groupchatform.h b/src/widget/form/groupchatform.h index 9ea0e0655..70399be80 100644 --- a/src/widget/form/groupchatform.h +++ b/src/widget/form/groupchatform.h @@ -34,6 +34,9 @@ public: private slots: void onSendTriggered(); + void onMicMuteToggle(); + void onVolMuteToggle(); + void onCallClicked(); protected: // drag & drop @@ -45,6 +48,7 @@ private: FlowLayout* namesListLayout; QLabel *nusersLabel; TabCompleter* tabber; + bool inCall; }; #endif // GROUPCHATFORM_H diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 5b0f2afe7..29812072f 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -985,7 +985,7 @@ Group *Widget::createGroup(int groupId) } QString groupName = QString("Groupchat #%1").arg(groupId); - Group* newgroup = GroupList::addGroup(groupId, groupName); + Group* newgroup = GroupList::addGroup(groupId, groupName, true); QLayout* layout = contactListWidget->getGroupLayout(); layout->addWidget(newgroup->widget); newgroup->widget->updateStatusLight();