mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-19 15:47:11 +02:00
Improved waveform display of recorded voice data.
This commit is contained in:
parent
e0cc3791ff
commit
d2defabd4b
2 changed files with 117 additions and 17 deletions
|
@ -46,6 +46,8 @@ constexpr auto kAudioVoiceMaxLength = 100 * 60; // 100 minutes
|
|||
constexpr auto kMaxSamples =
|
||||
::Media::Player::kDefaultFrequency * kAudioVoiceMaxLength;
|
||||
|
||||
constexpr auto kInactiveWaveformBarAlpha = int(255 * 0.6);
|
||||
|
||||
constexpr auto kPrecision = 10;
|
||||
|
||||
constexpr auto kLockArcAngle = 15.;
|
||||
|
@ -56,6 +58,10 @@ enum class FilterType {
|
|||
Cancel,
|
||||
};
|
||||
|
||||
[[nodiscard]] auto InactiveColor(const QColor &c) {
|
||||
return QColor(c.red(), c.green(), c.blue(), kInactiveWaveformBarAlpha);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto Progress(int low, int high) {
|
||||
return std::clamp(float64(low) / high, 0., 1.);
|
||||
}
|
||||
|
@ -102,6 +108,91 @@ enum class FilterType {
|
|||
int32(0));
|
||||
}
|
||||
|
||||
void PaintWaveform(
|
||||
Painter &p,
|
||||
not_null<const VoiceData*> voiceData,
|
||||
int availableWidth,
|
||||
const QColor &active,
|
||||
const QColor &inactive,
|
||||
float64 progress) {
|
||||
const auto wf = [&]() -> const VoiceWaveform* {
|
||||
if (voiceData->waveform.isEmpty()) {
|
||||
return nullptr;
|
||||
} else if (voiceData->waveform.at(0) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return &voiceData->waveform;
|
||||
}();
|
||||
|
||||
const auto samplesCount = wf
|
||||
? wf->size()
|
||||
: ::Media::Player::kWaveformSamplesCount;
|
||||
const auto activeWidth = std::round(availableWidth * progress);
|
||||
|
||||
const auto &barWidth = st::historyRecordWaveformBar;
|
||||
const auto barFullWidth = barWidth + st::msgWaveformSkip;
|
||||
const auto totalBarsCountF = (float)availableWidth / barFullWidth;
|
||||
const auto totalBarsCount = int(totalBarsCountF);
|
||||
const auto samplesPerBar = samplesCount / totalBarsCountF;
|
||||
const auto barNormValue = (wf ? voiceData->wavemax : 0) + 1;
|
||||
const auto maxDelta = st::msgWaveformMax - st::msgWaveformMin;
|
||||
const auto &bottom = st::msgWaveformMax;
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
int barNum = 0;
|
||||
const auto paintBar = [&](const auto &barValue) {
|
||||
const auto barHeight = st::msgWaveformMin + barValue;
|
||||
const auto barTop = (bottom - barHeight) / 2.;
|
||||
const auto barLeft = barNum * barFullWidth;
|
||||
const auto rect = [&](const auto &l, const auto &w) {
|
||||
return QRectF(l, barTop, w, barHeight);
|
||||
};
|
||||
|
||||
if ((barLeft < activeWidth) && (barLeft + barWidth > activeWidth)) {
|
||||
const auto leftWidth = activeWidth - barLeft;
|
||||
const auto rightWidth = barWidth - leftWidth;
|
||||
p.fillRect(rect(barLeft, leftWidth), active);
|
||||
p.fillRect(rect(activeWidth, rightWidth), inactive);
|
||||
} else {
|
||||
const auto &color = (barLeft >= activeWidth) ? inactive : active;
|
||||
p.fillRect(rect(barLeft, barWidth), color);
|
||||
}
|
||||
barNum++;
|
||||
};
|
||||
|
||||
auto barCounter = 0.;
|
||||
auto nextBarNum = 0;
|
||||
|
||||
auto sum = 0;
|
||||
auto maxValue = 0;
|
||||
|
||||
for (auto i = 0; i < samplesCount; i++) {
|
||||
const auto value = wf ? wf->at(i) : 0;
|
||||
if (i != nextBarNum) {
|
||||
maxValue = std::max(maxValue, value);
|
||||
sum += totalBarsCount;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compute height.
|
||||
sum += totalBarsCount - samplesCount;
|
||||
const auto isSumSmaller = (sum < (totalBarsCount + 1) / 2);
|
||||
if (isSumSmaller) {
|
||||
maxValue = std::max(maxValue, value);
|
||||
}
|
||||
const auto barValue = ((maxValue * maxDelta) + (barNormValue / 2))
|
||||
/ barNormValue;
|
||||
maxValue = isSumSmaller ? 0 : value;
|
||||
|
||||
const auto lastBarNum = nextBarNum;
|
||||
while (lastBarNum == nextBarNum) {
|
||||
barCounter += samplesPerBar;
|
||||
nextBarNum = (int)barCounter;
|
||||
paintBar(barValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class ListenWrap final {
|
||||
|
@ -127,6 +218,7 @@ private:
|
|||
bool isInPlayer() const;
|
||||
|
||||
int computeTopMargin(int height) const;
|
||||
QRect computeWaveformRect(const QRect ¢erRect) const;
|
||||
|
||||
not_null<Ui::RpWidget*> _parent;
|
||||
|
||||
|
@ -142,6 +234,8 @@ private:
|
|||
const int _durationWidth;
|
||||
const style::MediaPlayerButton &_playPauseSt;
|
||||
const base::unique_qptr<Ui::AbstractButton> _playPauseButton;
|
||||
const QColor _activeWaveformBar;
|
||||
const QColor _inactiveWaveformBar;
|
||||
|
||||
QRect _waveformBgRect;
|
||||
QRect _waveformBgFinalCenterRect;
|
||||
|
@ -176,6 +270,8 @@ ListenWrap::ListenWrap(
|
|||
, _durationWidth(_durationFont->width(_duration))
|
||||
, _playPauseSt(st::mediaPlayerButton)
|
||||
, _playPauseButton(base::make_unique_q<Ui::AbstractButton>(parent))
|
||||
, _activeWaveformBar(st::historyRecordVoiceFgActiveIcon->c)
|
||||
, _inactiveWaveformBar(InactiveColor(_activeWaveformBar))
|
||||
, _playPause(_playPauseSt, [=] { _playPauseButton->update(); }) {
|
||||
init();
|
||||
}
|
||||
|
@ -203,6 +299,7 @@ void ListenWrap::init() {
|
|||
final.x() - (final.height() - play.width()) / 2,
|
||||
final.y());
|
||||
}
|
||||
_waveformFgRect = computeWaveformRect(_waveformBgFinalCenterRect);
|
||||
}, _lifetime);
|
||||
|
||||
_parent->paintRequest(
|
||||
|
@ -265,27 +362,17 @@ void ListenWrap::init() {
|
|||
|
||||
// Waveform paint.
|
||||
{
|
||||
const auto computeRect = [&] {
|
||||
const auto &play = _playPauseSt.playOuter;
|
||||
const auto top = computeTopMargin(st::msgWaveformMax);
|
||||
const auto left = play.width() / 2 + halfHeight;
|
||||
const auto right = st::historyRecordWaveformLeftSkip
|
||||
+ _durationWidth;
|
||||
_waveformFgRect = bgCenterRect.marginsRemoved(
|
||||
style::margins(left, top, right, top));
|
||||
return _waveformFgRect;
|
||||
};
|
||||
const auto rect = (progress == 1.)
|
||||
? _waveformFgRect
|
||||
: computeRect();
|
||||
: computeWaveformRect(bgCenterRect);
|
||||
if (rect.width() > 0) {
|
||||
p.translate(rect.topLeft());
|
||||
HistoryDocumentVoice::PaintWaveform(
|
||||
PaintWaveform(
|
||||
p,
|
||||
_voiceData.get(),
|
||||
rect.width(),
|
||||
false,
|
||||
false,
|
||||
_activeWaveformBar,
|
||||
_inactiveWaveformBar,
|
||||
_playProgress.current());
|
||||
p.resetTransform();
|
||||
}
|
||||
|
@ -406,7 +493,7 @@ void ListenWrap::initPlayProgress() {
|
|||
} else {
|
||||
_playProgress.update(std::min(dt, 1.), anim::linear);
|
||||
}
|
||||
_parent->update();
|
||||
_parent->update(_waveformFgRect);
|
||||
return (dt < 1.);
|
||||
};
|
||||
animation->init(std::move(animationCallback));
|
||||
|
@ -448,7 +535,7 @@ void ListenWrap::initPlayProgress() {
|
|||
const auto isRelease = (type == QEvent::MouseButtonRelease);
|
||||
if (isRelease || isMove) {
|
||||
_playProgress = anim::value(progress, progress);
|
||||
_parent->update();
|
||||
_parent->update(_waveformFgRect);
|
||||
if (isRelease) {
|
||||
instance()->finishSeeking(voice, progress);
|
||||
*isPressed = false;
|
||||
|
@ -469,6 +556,14 @@ bool ListenWrap::isInPlayer() const {
|
|||
return isInPlayer(::Media::Player::instance()->getState(Type::Voice));
|
||||
}
|
||||
|
||||
QRect ListenWrap::computeWaveformRect(const QRect ¢erRect) const {
|
||||
const auto top = computeTopMargin(st::msgWaveformMax);
|
||||
const auto left = (_playPauseSt.playOuter.width() + centerRect.height())
|
||||
/ 2;
|
||||
const auto right = st::historyRecordWaveformRightSkip + _durationWidth;
|
||||
return centerRect.marginsRemoved(style::margins(left, top, right, top));
|
||||
}
|
||||
|
||||
int ListenWrap::computeTopMargin(int height) const {
|
||||
return (_waveformBgRect.height() - height) / 2;
|
||||
}
|
||||
|
@ -892,6 +987,9 @@ void VoiceRecordBar::init() {
|
|||
_listen->requestPaintProgress(value);
|
||||
_level->requestPaintProgress(to - value);
|
||||
update();
|
||||
if (to == value) {
|
||||
_recordingLifetime.destroy();
|
||||
}
|
||||
};
|
||||
_showListenAnimation.start(std::move(callback), 0., to, duration);
|
||||
}, lifetime());
|
||||
|
|
|
@ -385,9 +385,11 @@ historyRecordDelete: IconButton(historyAttach) {
|
|||
icon: icon {{ "info_media_delete", historyComposeIconFg }};
|
||||
iconOver: icon {{ "info_media_delete", historyComposeIconFgOver }};
|
||||
}
|
||||
historyRecordWaveformLeftSkip: 5px;
|
||||
historyRecordWaveformRightSkip: 10px;
|
||||
historyRecordWaveformBgMargins: margins(5px, 7px, 5px, 7px);
|
||||
|
||||
historyRecordWaveformBar: 3px;
|
||||
|
||||
historyRecordLockPosition: point(7px, 35px);
|
||||
|
||||
historySilentToggle: IconButton(historyBotKeyboardShow) {
|
||||
|
|
Loading…
Add table
Reference in a new issue