Voice fixes: missing waveform, 0 duration, text

This commit is contained in:
Eduard Kuzmenko 2023-03-02 17:24:15 +04:00
parent d9333833a4
commit 14e826e19e
4 changed files with 73 additions and 43 deletions

View File

@ -73,39 +73,21 @@ export function decodeWaveform(waveform: Uint8Array | number[]) {
result = new Uint8Array([]); result = new Uint8Array([]);
} }
/* var byteIndex = (valueCount - 1) / 8 | 0;
var bitShift = (valueCount - 1) % 8;
if(byteIndex === waveform.length - 1) {
var value = waveform[byteIndex];
} else {
var value = dataView.getUint16(byteIndex, true);
}
console.log('decoded waveform, setting last value:', value, byteIndex, bitShift);
result[valueCount - 1] = (value >> bitShift) & 0b00011111; */
return result; return result;
} }
function createWaveformBars(waveform: Uint8Array, duration: number) { function createWaveformBars(waveform: Uint8Array, duration: number) {
const barWidth = 2; const barWidth = 2;
const barMargin = 2; // mediaSizes.isMobile ? 2 : 1; const barMargin = 2;
const barHeightMin = 4; // mediaSizes.isMobile ? 3 : 2; const barHeightMin = 4;
const barHeightMax = mediaSizes.isMobile ? 16 : 23; const barHeightMax = mediaSizes.isMobile ? 16 : 23;
// const availW = 150; //mediaSizes.isMobile ? 152 : 190;
const minW = mediaSizes.isMobile ? 152 : 190; const minW = mediaSizes.isMobile ? 152 : 190;
const maxW = mediaSizes.isMobile ? 190 : 256; const maxW = mediaSizes.isMobile ? 190 : 256;
const availW = clamp(duration / 60 * maxW, minW, maxW); // mediaSizes.isMobile ? 152 : 224; const availW = clamp(duration / 60 * maxW, minW, maxW);
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.classList.add('audio-waveform-bars');
svg.setAttributeNS(null, 'width', '' + availW);
svg.setAttributeNS(null, 'height', '' + barHeightMax);
svg.setAttributeNS(null, 'viewBox', `0 0 ${availW} ${barHeightMax}`);
// console.log('decoded waveform:', waveform);
const normValue = Math.max(...waveform); const normValue = Math.max(...waveform);
const wfSize = waveform.length ? waveform.length : 100; const wfSize = waveform.length;
const barCount = Math.min((availW / (barWidth + barMargin)) | 0, wfSize); const barCount = Math.min((availW / (barWidth + barMargin)) | 0, wfSize);
let maxValue = 0; let maxValue = 0;
@ -122,9 +104,7 @@ function createWaveformBars(waveform: Uint8Array, duration: number) {
const bar_value = Math.max(((maxValue * maxDelta) + ((normValue + 1) / 2)) / (normValue + 1), barHeightMin); const bar_value = Math.max(((maxValue * maxDelta) + ((normValue + 1) / 2)) / (normValue + 1), barHeightMin);
const h = ` const h = `<rect class="audio-waveform-bar" x="${barX}" y="${barHeightMax - bar_value}" width="${barWidth}" height="${bar_value}" rx="1" ry="1"></rect>`;
<rect class="audio-waveform-bar" x="${barX}" y="${barHeightMax - bar_value}" width="${barWidth}" height="${bar_value}" rx="1" ry="1"></rect>
`;
html += h; html += h;
barX += barWidth + barMargin; barX += barWidth + barMargin;
@ -141,11 +121,24 @@ function createWaveformBars(waveform: Uint8Array, duration: number) {
} }
} }
const container = document.createElement('div'); let container: HTMLElement, svg: SVGSVGElement;
container.classList.add('audio-waveform');
container.append(svg); if(!html) {
} else {
container = document.createElement('div');
container.classList.add('audio-waveform');
svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.classList.add('audio-waveform-bars');
svg.setAttributeNS(null, 'width', '' + availW);
svg.setAttributeNS(null, 'height', '' + barHeightMax);
svg.setAttributeNS(null, 'viewBox', `0 0 ${availW} ${barHeightMax}`);
svg.insertAdjacentHTML('beforeend', html);
container.append(svg);
}
svg.insertAdjacentHTML('beforeend', html);
return {svg, container, availW}; return {svg, container, availW};
} }
@ -164,13 +157,19 @@ async function wrapVoiceMessage(audioEl: AudioElement) {
const {svg, container: svgContainer, availW} = createWaveformBars(waveform, doc.duration); const {svg, container: svgContainer, availW} = createWaveformBars(waveform, doc.duration);
const fakeSvgContainer = svgContainer.cloneNode(true) as HTMLElement; let fakeSvgContainer: HTMLElement;
fakeSvgContainer.classList.add('audio-waveform-fake'); if(svgContainer) {
svgContainer.classList.add('audio-waveform-background'); fakeSvgContainer = svgContainer.cloneNode(true) as HTMLElement;
fakeSvgContainer.classList.add('audio-waveform-fake');
svgContainer.classList.add('audio-waveform-background');
}
const waveformContainer = document.createElement('div'); const waveformContainer = document.createElement('div');
waveformContainer.classList.add('audio-waveform-container'); waveformContainer.classList.add('audio-waveform-container');
waveformContainer.append(svgContainer, fakeSvgContainer);
if(svgContainer) {
waveformContainer.append(svgContainer, fakeSvgContainer);
}
const timeDiv = document.createElement('div'); const timeDiv = document.createElement('div');
timeDiv.classList.add('audio-time'); timeDiv.classList.add('audio-time');
@ -224,7 +223,14 @@ async function wrapVoiceMessage(audioEl: AudioElement) {
audioEl.append(speechRecognitionDiv); audioEl.append(speechRecognitionDiv);
} }
let progress = svg as any as HTMLElement; let progress = svg as any as HTMLElement, progressLine: MediaProgressLine;
if(!progress) {
progressLine = new MediaProgressLine({
streamable: doc.supportsStreaming
});
waveformContainer.append(progressLine.container);
}
const onLoad = () => { const onLoad = () => {
let audio = audioEl.audio; let audio = audioEl.audio;
@ -238,7 +244,9 @@ async function wrapVoiceMessage(audioEl: AudioElement) {
}; };
const onTimeUpdate = () => { const onTimeUpdate = () => {
fakeSvgContainer.style.width = (audio.currentTime / audio.duration * 100) + '%'; if(fakeSvgContainer) {
fakeSvgContainer.style.width = (audio.currentTime / audio.duration * 100) + '%';
}
}; };
if(!audio.paused || (audio.currentTime > 0 && audio.currentTime !== audio.duration)) { if(!audio.paused || (audio.currentTime > 0 && audio.currentTime !== audio.duration)) {
@ -250,7 +258,7 @@ async function wrapVoiceMessage(audioEl: AudioElement) {
audioEl.addAudioListener('ended', throttledTimeUpdate); audioEl.addAudioListener('ended', throttledTimeUpdate);
audioEl.addAudioListener('play', setAnimation); audioEl.addAudioListener('play', setAnimation);
audioEl.readyPromise.then(() => { progress && audioEl.readyPromise.then(() => {
let mousedown = false, mousemove = false; let mousedown = false, mousemove = false;
progress.addEventListener('mouseleave', (e) => { progress.addEventListener('mouseleave', (e) => {
if(mousedown) { if(mousedown) {
@ -298,8 +306,10 @@ async function wrapVoiceMessage(audioEl: AudioElement) {
} }
}, noop); }, noop);
!progress && progressLine.setMedia(audio);
return () => { return () => {
progress.remove(); progress?.remove();
progress = null; progress = null;
audio = null; audio = null;
}; };
@ -495,7 +505,10 @@ export default class AudioElement extends HTMLElement {
const isOutgoing = this.message.pFlags.is_outgoing; const isOutgoing = this.message.pFlags.is_outgoing;
const uploadingFileName = this.message?.uploadingFileName; const uploadingFileName = this.message?.uploadingFileName;
const durationStr = toHHMMSS(doc.duration | 0); const getDurationStr = () => {
const duration = this.audio && this.audio.readyState >= this.audio.HAVE_CURRENT_DATA ? this.audio.duration : doc.duration;
return toHHMMSS(duration | 0);
};
this.innerHTML = ` this.innerHTML = `
<div class="audio-toggle audio-ico"> <div class="audio-toggle audio-ico">
@ -523,7 +536,7 @@ export default class AudioElement extends HTMLElement {
const onTypeLoad = await (isVoice ? wrapVoiceMessage(this) : wrapAudio(this)); const onTypeLoad = await (isVoice ? wrapVoiceMessage(this) : wrapAudio(this));
const audioTimeDiv = this.querySelector('.audio-time') as HTMLDivElement; const audioTimeDiv = this.querySelector('.audio-time') as HTMLDivElement;
audioTimeDiv.innerHTML = durationStr; audioTimeDiv.textContent = getDurationStr();
const onLoad = this.onLoad = (autoload: boolean) => { const onLoad = this.onLoad = (autoload: boolean) => {
this.onLoad = undefined; this.onLoad = undefined;
@ -538,7 +551,7 @@ export default class AudioElement extends HTMLElement {
this.onTypeDisconnect = onTypeLoad(); this.onTypeDisconnect = onTypeLoad();
const getTimeStr = () => toHHMMSS(audio.currentTime | 0) + (isVoice ? (' / ' + durationStr) : ''); const getTimeStr = () => toHHMMSS(audio.currentTime | 0) + (isVoice ? (' / ' + getDurationStr()) : '');
const onPlay = () => { const onPlay = () => {
audioTimeDiv.innerText = getTimeStr(); audioTimeDiv.innerText = getTimeStr();
@ -573,7 +586,7 @@ export default class AudioElement extends HTMLElement {
this.addAudioListener('ended', () => { this.addAudioListener('ended', () => {
toggle.classList.remove('playing'); toggle.classList.remove('playing');
audioTimeDiv.innerText = durationStr; audioTimeDiv.innerText = getDurationStr();
}); });
this.addAudioListener('timeupdate', () => { this.addAudioListener('timeupdate', () => {

2
src/global.d.ts vendored
View File

@ -65,7 +65,7 @@ declare global {
'USER_ALREADY_PARTICIPANT' | 'USERNAME_INVALID' | 'USERNAME_PURCHASE_AVAILABLE' | 'USERNAMES_ACTIVE_TOO_MUCH' | 'USER_ALREADY_PARTICIPANT' | 'USERNAME_INVALID' | 'USERNAME_PURCHASE_AVAILABLE' | 'USERNAMES_ACTIVE_TOO_MUCH' |
'BOT_INVALID' | 'USERNAME_NOT_OCCUPIED' | 'PINNED_TOO_MUCH' | 'LOCATION_INVALID' | 'BOT_INVALID' | 'USERNAME_NOT_OCCUPIED' | 'PINNED_TOO_MUCH' | 'LOCATION_INVALID' |
'FILE_ID_INVALID' | 'CHANNEL_FORUM_MISSING' | 'TRANSCRIPTION_FAILED' | 'USER_NOT_PARTICIPANT' | 'FILE_ID_INVALID' | 'CHANNEL_FORUM_MISSING' | 'TRANSCRIPTION_FAILED' | 'USER_NOT_PARTICIPANT' |
'PEER_ID_INVALID'; 'PEER_ID_INVALID' | 'MSG_VOICE_MISSING';
type ErrorType = LocalErrorType | ServerErrorType; type ErrorType = LocalErrorType | ServerErrorType;

View File

@ -549,7 +549,7 @@ export class AppMessagesManager extends AppManager {
return result; return result;
}, },
processError: (error) => { processError: (error) => {
if(error.type === 'TRANSCRIPTION_FAILED') { if(error.type === 'TRANSCRIPTION_FAILED' || error.type === 'MSG_VOICE_MISSING') {
process({ process({
_: 'messages.transcribedAudio', _: 'messages.transcribedAudio',
transcription_id: 0, transcription_id: 0,

View File

@ -406,6 +406,8 @@
position: relative; position: relative;
height: 23px; height: 23px;
margin-top: 1px; margin-top: 1px;
display: flex;
align-items: center;
} }
&-bar { &-bar {
@ -576,6 +578,21 @@
align-items: center; align-items: center;
} }
.progress-line {
margin-top: 4px;
margin-inline-start: 0;
&__filled:after {
opacity: 0;
}
}
// .audio-toggle:not(.playing) + .audio-waveform-container {
// .progress-line__filled:after {
// opacity: 0;
// }
// }
@include respond-to(handhelds) { @include respond-to(handhelds) {
.audio-time { .audio-time {
margin-top: .3125rem; margin-top: .3125rem;