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 }