feat: integrate Opus codec into lib/libopus + voice messages in chatgui
- Add lib/libopus/ (opus v1.6.1 embedded source: CELT 18 .c, SILK 77 .c, SILK float 28 .c, OPUS core 14 .c) - Add lib/opus_codec.h/.c (wrapper: encoder, decoder, raw PCM int16) - Add tests/test_opus_codec.c (7 tests: silence, sine, compression, PLC, null, defaults, multi-frame) - chatgui: AudioRecorder (miniaudio capture, 48kHz mono) - chatgui: VoiceEncoder (PCM→Opus file with custom container) - chatgui: VoicePlayer (Opus→PCM playback via SoundManager) - chatgui: PTT button in InputBar, voice message send/receive in MessageList - chatgui: AudioDeviceSettingsPage (device selection, persisted in ui_state) - chatgui: MessageDelegate voice message rendering (play button + duration) - Update autotools: configure.ac, Makefile.am, src/Makefile.am, tests/Makefile.am - Update CMake: chatgui CMakeLists.txt, libutun CMakeLists.txt (link libopus_internal.a)
This commit is contained in:
1 parent
4a23593020
commit
4fde0b0325
260 files changed
+72665
-16
No files matched your search
@@ -67,6 +67,35 @@ MessageDelegate::Layout MessageDelegate::calcLayout(
|
||||
QString text = index.data(MsgTextRole).toString();
|
||||
L.isOutgoing = index.data(MsgIsOutgoingRole).toBool();
|
||||
|
||||
/* Voice message detection */
|
||||
QString contentType = index.data(MsgContentTypeRole).toString();
|
||||
L.isVoice = (contentType == "audio/opus");
|
||||
if (L.isVoice) {
|
||||
L.voiceDuration = index.data(MsgVoiceDurationRole).toString();
|
||||
QFontMetrics fm(option.font);
|
||||
int lineH = fm.lineSpacing();
|
||||
L.totalHeight = qMax(lineH * 2 + kGap * 2, kAvatar) + kItemGap;
|
||||
L.bubbleWidth = 160;
|
||||
int contentEdge = L.isOutgoing
|
||||
? L.viewWidth - (L.narrow ? kMarginH : (kMarginH + kAvatar + kGap))
|
||||
: (L.narrow ? kMarginH : (kMarginH + kAvatar + kGap));
|
||||
if (!L.isOutgoing)
|
||||
L.bubbleRect = QRect(contentEdge, 0, L.bubbleWidth, L.totalHeight - kItemGap);
|
||||
else
|
||||
L.bubbleRect = QRect(contentEdge - L.bubbleWidth, 0, L.bubbleWidth, L.totalHeight - kItemGap);
|
||||
L.voicePlayBtnRect = QRect(L.bubbleRect.left() + kPadH, L.bubbleRect.top() + kPadTop + 4, 28, 28);
|
||||
L.voiceDurationRect = QRect(L.bubbleRect.left() + kPadH + 36, L.bubbleRect.top() + kPadTop + 4,
|
||||
L.bubbleWidth - kPadH * 2 - 36, 28);
|
||||
if (!L.narrow) {
|
||||
int avatarY = L.bubbleRect.bottom() - kAvatar;
|
||||
if (!L.isOutgoing)
|
||||
L.avatarRect = QRect(kMarginH, avatarY, kAvatar, kAvatar);
|
||||
else
|
||||
L.avatarRect = QRect(L.viewWidth - kMarginH - kAvatar, avatarY, kAvatar, kAvatar);
|
||||
}
|
||||
return L;
|
||||
}
|
||||
|
||||
L.emojiCount = detectEmojiOnly(text);
|
||||
L.emojiOnly = (L.emojiCount >= 1);
|
||||
|
||||
@@ -449,6 +478,43 @@ void MessageDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio
|
||||
return;
|
||||
}
|
||||
|
||||
if (L.isVoice) {
|
||||
drawBubble(painter, L, option);
|
||||
if (!L.narrow) drawAvatar(painter, L, index);
|
||||
|
||||
/* Play button */
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->setBrush(QColor(0x33, 0x90, 0xEC));
|
||||
QPainterPath playPath;
|
||||
QRectF playR = L.voicePlayBtnRect;
|
||||
qreal cx = playR.center().x() - playR.width() / 6.0;
|
||||
qreal cy = playR.center().y();
|
||||
qreal hs = playR.height() / 2.5;
|
||||
playPath.moveTo(cx, cy - hs);
|
||||
playPath.lineTo(cx + hs * 1.5, cy);
|
||||
playPath.lineTo(cx, cy + hs);
|
||||
playPath.closeSubpath();
|
||||
painter->drawPath(playPath);
|
||||
|
||||
/* Duration text */
|
||||
painter->setPen(option.palette.text().color());
|
||||
painter->setFont(option.font);
|
||||
if (!L.voiceDuration.isEmpty())
|
||||
painter->drawText(L.voiceDurationRect, Qt::AlignLeft | Qt::AlignVCenter, L.voiceDuration);
|
||||
|
||||
/* Time */
|
||||
QFont timeFont = option.font;
|
||||
timeFont.setPointSize(timeFont.pointSize() - 1);
|
||||
painter->setFont(timeFont);
|
||||
painter->setPen(option.palette.text().color().lighter(150));
|
||||
QString timeStr = index.data(MsgTimeRole).toString();
|
||||
QRect timeR = QRect(L.bubbleRect.right() - kPadH - 50, L.voicePlayBtnRect.bottom() + 4, 50, 16);
|
||||
painter->drawText(timeR, Qt::AlignRight | Qt::AlignVCenter, timeStr);
|
||||
|
||||
painter->restore();
|
||||
return;
|
||||
}
|
||||
|
||||
auto *cv = qobject_cast<const ChatView *>(option.widget);
|
||||
bool singleTextSel = false;
|
||||
bool multiRowSel = false;
|
||||
|
||||
Reference in new issue
Block a user