Voice fixes: missing waveform, 0 duration, text
This commit is contained in:
parent
d9333833a4
commit
14e826e19e
|
@ -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', () => {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue