File indexing completed on 2025-02-23 04:35:41
0001 /* 0002 SPDX-FileCopyrightText: 2010-2022 Mladen Milinkovic <max@smoothware.net> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "wavebuffer.h" 0008 0009 #include "application.h" 0010 #include "gui/waveform/waveformwidget.h" 0011 #include "gui/waveform/zoombuffer.h" 0012 0013 #include <QProgressBar> 0014 #include <QScrollBar> 0015 #include <QtMath> 0016 0017 #define MAX_WINDOW_ZOOM 3000 // TODO: calculate this when receiving stream data and do sample rate conversion 0018 //#define SAMPLE_RATE 8000 0019 //#define SAMPLE_RATE_MILLIS (SAMPLE_RATE / 1000) 0020 //#define DRAG_TOLERANCE (double(10 * m_samplesPerPixel / SAMPLE_RATE_MILLIS)) 0021 0022 0023 namespace SubtitleComposer { 0024 struct WaveformFrame { 0025 explicit WaveformFrame(quint8 shift, quint8 channels) 0026 : offset(0), 0027 sampleShift(shift), 0028 frameSize((1 << sampleShift) * channels), 0029 overflow(0), 0030 val(new qint32[channels]) 0031 { 0032 } 0033 0034 virtual ~WaveformFrame() { 0035 delete[] val; 0036 } 0037 0038 quint32 offset; 0039 quint8 sampleShift; 0040 quint16 frameSize; 0041 quint16 overflow; 0042 qint32 *val; 0043 }; 0044 } 0045 0046 0047 using namespace SubtitleComposer; 0048 0049 WaveBuffer::WaveBuffer(WaveformWidget *parent) 0050 : QObject(parent), 0051 m_wfWidget(parent), 0052 m_stream(new StreamProcessor(this)), 0053 m_waveformDuration(0), 0054 m_waveformChannels(0), 0055 m_waveformChannelSize(0), 0056 m_waveform(nullptr), 0057 m_samplesSec(0), 0058 m_wfFrame(nullptr), 0059 m_zoomBuffer(new ZoomBuffer(this)) 0060 { 0061 connect(m_stream, &StreamProcessor::streamProgress, this, &WaveBuffer::onStreamProgress); 0062 connect(m_stream, &StreamProcessor::streamFinished, this, &WaveBuffer::onStreamFinished); 0063 // Using Qt::DirectConnection here makes WaveBuffer::onStreamData() to execute in SpeechProcessor's thread 0064 connect(m_stream, &StreamProcessor::audioDataAvailable, this, &WaveBuffer::onStreamData, Qt::DirectConnection); 0065 } 0066 0067 quint32 0068 WaveBuffer::millisPerPixel() const 0069 { 0070 return m_zoomBuffer->samplesPerPixel() * 1000 / m_samplesSec; 0071 } 0072 0073 quint32 0074 WaveBuffer::samplesAvailable() const 0075 { 0076 return m_wfFrame ? m_wfFrame->offset : m_waveformChannelSize; 0077 } 0078 0079 void 0080 WaveBuffer::setAudioStream(const QString &mediaFile, int audioStream) 0081 { 0082 m_waveformDuration = 0; 0083 0084 static WaveFormat waveFormat(0, 0, sizeof(SAMPLE_TYPE) * 8, true); 0085 if(m_stream->open(mediaFile) && m_stream->initAudio(audioStream, waveFormat)) 0086 m_stream->start(); 0087 } 0088 0089 void 0090 WaveBuffer::setNullAudioStream(quint64 msecVideoLength) 0091 { 0092 clearAudioStream(); 0093 0094 m_waveformDuration = msecVideoLength / 1000; 0095 0096 emit waveformUpdated(); 0097 } 0098 0099 void 0100 WaveBuffer::clearAudioStream() 0101 { 0102 m_stream->close(); 0103 0104 if(m_waveform) { 0105 m_zoomBuffer->setWaveform(nullptr); 0106 for(quint32 i = 0; i < m_waveformChannels; i++) 0107 delete[] m_waveform[i]; 0108 delete[] m_waveform; 0109 m_waveform = nullptr; 0110 m_waveformChannelSize = 0; 0111 m_waveformChannels = 0; 0112 m_samplesSec = 0; 0113 } 0114 } 0115 0116 void 0117 WaveBuffer::onStreamProgress(quint64 msecPos, quint64 msecLength) 0118 { 0119 if(!m_waveformDuration) { 0120 m_waveformDuration = msecLength / 1000; 0121 m_wfWidget->m_progressBar->setRange(0, m_waveformDuration); 0122 m_wfWidget->m_progressWidget->show(); 0123 m_wfWidget->m_scrollBar->setRange(0, m_waveformDuration * 1000 - m_wfWidget->windowSizeInner()); 0124 } 0125 m_wfWidget->m_progressBar->setValue(msecPos / 1000); 0126 } 0127 0128 void 0129 WaveBuffer::onStreamFinished() 0130 { 0131 m_wfWidget->m_progressWidget->hide(); 0132 if(m_wfFrame) { 0133 m_waveformChannelSize = m_wfFrame->offset; 0134 delete m_wfFrame; 0135 m_wfFrame = nullptr; 0136 } 0137 } 0138 0139 inline static SAMPLE_TYPE 0140 scaleSample(SAMPLE_TYPE sample) 0141 { 0142 static const qreal valMax = qreal(SAMPLE_MAX - SAMPLE_MIN) / 2.; 0143 return qSqrt(qreal(sample) / valMax) * SAMPLE_MAX; 0144 } 0145 0146 void 0147 WaveBuffer::onStreamData(const void *buffer, qint32 size, const WaveFormat *waveFormat, const qint64 msecStart, const qint64 /*msecDuration*/) 0148 { 0149 // make sure WaveBuffer::onStreamProgress() signal was processed since we're in different thread 0150 while(!m_waveformDuration) { 0151 QThread::yieldCurrentThread(); 0152 if(m_stream->isInterruptionRequested()) 0153 return; 0154 } 0155 0156 if(!m_waveformChannels) { 0157 m_samplesSec = waveFormat->sampleRate(); 0158 quint8 sampleShift = 0; 0159 while(m_samplesSec > MAX_WINDOW_ZOOM) { 0160 m_samplesSec >>= 1; 0161 sampleShift++; 0162 } 0163 m_waveformChannels = waveFormat->channels(); 0164 m_waveformChannelSize = m_samplesSec * (m_waveformDuration + 60); // added 60sec as duration might be wrong 0165 m_waveform = new SAMPLE_TYPE *[m_waveformChannels]; 0166 for(quint32 i = 0; i < m_waveformChannels; i++) 0167 m_waveform[i] = new SAMPLE_TYPE[m_waveformChannelSize]; 0168 0169 m_wfFrame = new WaveformFrame(sampleShift, m_waveformChannels); 0170 0171 m_zoomBuffer->setWaveform(m_waveform); 0172 0173 emit waveformUpdated(); 0174 } 0175 0176 Q_ASSERT(waveFormat->bitsPerSample() == sizeof(SAMPLE_TYPE) * 8); 0177 0178 const SAMPLE_TYPE *sample = reinterpret_cast<const SAMPLE_TYPE *>(buffer); 0179 0180 { // handle overlaps and holes between buffers (tested streams had ~2ms error - might be useless) 0181 const quint32 inStartOffset = qMax(0LL, msecStart) * m_samplesSec / 1000; 0182 if(inStartOffset < m_wfFrame->offset) { 0183 // overwrite part of local buffer 0184 m_wfFrame->offset = inStartOffset; 0185 } else if(inStartOffset > m_wfFrame->offset) { 0186 // pad hole in local buffer 0187 quint32 i = m_wfFrame->offset; 0188 if(i > 0) { 0189 for(; i < inStartOffset; i++) { 0190 for(quint32 c = 0; c < m_waveformChannels; c++) 0191 m_waveform[c][i] = m_waveform[c][i - 1]; 0192 } 0193 } else { 0194 for(quint32 c = 0; c < m_waveformChannels; c++) 0195 memset(m_waveform[c], 0, inStartOffset * sizeof(SAMPLE_TYPE)); 0196 } 0197 m_wfFrame->offset = inStartOffset; 0198 } 0199 } 0200 0201 Q_ASSERT(m_waveformChannels > 0); 0202 0203 quint32 len = size / sizeof(SAMPLE_TYPE); 0204 0205 if(m_wfFrame->overflow) { 0206 const quint32 overflowFrameSize = qMin(m_wfFrame->overflow + len, quint32(m_wfFrame->frameSize)); 0207 0208 quint32 c = m_wfFrame->overflow; 0209 for(; c < m_waveformChannels; c++) 0210 m_wfFrame->val[c] = *sample++; 0211 for(; c < overflowFrameSize; c++) 0212 m_wfFrame->val[c % m_waveformChannels] += *sample++; 0213 for(c = 0; c < m_waveformChannels; c++) 0214 m_waveform[c][m_wfFrame->offset] = scaleSample(m_wfFrame->val[c] >> m_wfFrame->sampleShift); 0215 0216 len -= overflowFrameSize - m_wfFrame->overflow; 0217 if(overflowFrameSize < m_wfFrame->frameSize) { 0218 // no more data 0219 Q_ASSERT(len == 0); 0220 m_wfFrame->overflow = overflowFrameSize; 0221 return; 0222 } 0223 m_wfFrame->offset++; 0224 } 0225 0226 if(m_wfFrame->offset + (len >> m_wfFrame->sampleShift) >= m_waveformChannelSize) // make sure we don't overflow 0227 len = (m_waveformChannelSize - m_wfFrame->offset - 1) << m_wfFrame->sampleShift; 0228 0229 while(len > m_wfFrame->frameSize) { 0230 quint32 c = 0; 0231 for(; c < m_waveformChannels; c++) 0232 m_wfFrame->val[c] = *sample++; 0233 for(; c < m_wfFrame->frameSize; c++) 0234 m_wfFrame->val[c % m_waveformChannels] += *sample++; 0235 for(c = 0; c < m_waveformChannels; c++) 0236 m_waveform[c][m_wfFrame->offset] = scaleSample(m_wfFrame->val[c] >> m_wfFrame->sampleShift); 0237 len -= m_wfFrame->frameSize; 0238 m_wfFrame->offset++; 0239 } 0240 m_wfFrame->overflow = len; 0241 }