almost there now
This commit is contained in:
parent
4c6eb8dfe9
commit
d674c25a29
|
|
@ -312,47 +312,6 @@ void AudioEngine::onFinished() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef IS_MOBILE
|
|
||||||
// 2. Hilbert Transform — process one channel at a time to minimize
|
|
||||||
// peak memory. FFTW uses 4 buffers per channel instead of 8.
|
|
||||||
auto finalData = std::make_shared<TrackData>();
|
|
||||||
finalData->pcmData = pcmSnap;
|
|
||||||
finalData->sampleRate = sr;
|
|
||||||
finalData->valid = true;
|
|
||||||
finalData->complexData.resize(totalFloats);
|
|
||||||
|
|
||||||
BlockHilbert blockHilbert;
|
|
||||||
// Reusable input buffer (one channel at a time)
|
|
||||||
std::vector<double> input(totalFrames);
|
|
||||||
|
|
||||||
// Left channel: build input, transform, copy to complexData, free result
|
|
||||||
for (size_t i = 0; i < static_cast<size_t>(totalFrames); ++i)
|
|
||||||
input[i] = static_cast<double>(rawFloats[i * 2]);
|
|
||||||
{
|
|
||||||
auto analytic = blockHilbert.hilbertTransformSingle(input);
|
|
||||||
for (size_t i = 0; i < static_cast<size_t>(totalFrames); ++i)
|
|
||||||
finalData->complexData[i * 2] = analytic[i];
|
|
||||||
} // analytic freed
|
|
||||||
|
|
||||||
// Right channel: reuse input buffer
|
|
||||||
for (size_t i = 0; i < static_cast<size_t>(totalFrames); ++i)
|
|
||||||
input[i] = static_cast<double>(rawFloats[i * 2 + 1]);
|
|
||||||
{
|
|
||||||
auto analytic = blockHilbert.hilbertTransformSingle(input);
|
|
||||||
for (size_t i = 0; i < static_cast<size_t>(totalFrames); ++i)
|
|
||||||
finalData->complexData[i * 2 + 1] = analytic[i];
|
|
||||||
} // analytic freed
|
|
||||||
|
|
||||||
// Free input buffer
|
|
||||||
{ std::vector<double>().swap(input); }
|
|
||||||
|
|
||||||
// Notify Analyzer with the complete data
|
|
||||||
if (self) {
|
|
||||||
QMetaObject::invokeMethod(self, "trackDataChanged",
|
|
||||||
Qt::QueuedConnection,
|
|
||||||
Q_ARG(std::shared_ptr<TrackData>, finalData));
|
|
||||||
}
|
|
||||||
#endif // !IS_MOBILE
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -504,9 +463,7 @@ void AudioAnalyzer::setTrackData(std::shared_ptr<TrackData> data) {
|
||||||
p->setSampleRate(m_data->sampleRate);
|
p->setSampleRate(m_data->sampleRate);
|
||||||
for (auto p : m_deepProcessors)
|
for (auto p : m_deepProcessors)
|
||||||
p->setSampleRate(m_data->sampleRate);
|
p->setSampleRate(m_data->sampleRate);
|
||||||
#ifdef IS_MOBILE
|
|
||||||
m_hilbertNeedsReset = true;
|
m_hilbertNeedsReset = true;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -515,13 +472,11 @@ void AudioAnalyzer::setAtomicPositionRef(std::atomic<double> *posRef) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioAnalyzer::setDspParams(int frameSize, int hopSize) {
|
void AudioAnalyzer::setDspParams(int frameSize, int hopSize) {
|
||||||
#ifdef IS_MOBILE
|
|
||||||
if (frameSize != m_hilbertFftSize || hopSize != m_hilbertHopSize) {
|
if (frameSize != m_hilbertFftSize || hopSize != m_hilbertHopSize) {
|
||||||
m_hilbertFftSize = frameSize;
|
m_hilbertFftSize = frameSize;
|
||||||
m_hilbertHopSize = hopSize;
|
m_hilbertHopSize = hopSize;
|
||||||
m_hilbertNeedsReset = true;
|
m_hilbertNeedsReset = true;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
m_frameSize = frameSize;
|
m_frameSize = frameSize;
|
||||||
m_hopSize = hopSize;
|
m_hopSize = hopSize;
|
||||||
for (auto p : m_processors)
|
for (auto p : m_processors)
|
||||||
|
|
@ -592,8 +547,7 @@ void AudioAnalyzer::processLoop() {
|
||||||
|
|
||||||
double pos = m_posRef->load();
|
double pos = m_posRef->load();
|
||||||
|
|
||||||
#ifdef IS_MOBILE
|
// Streaming RealtimeHilbert produces complex frames on-the-fly
|
||||||
// Mobile path: streaming RealtimeHilbert produces complex frames on-the-fly
|
|
||||||
size_t totalSamples = m_data->pcmData.size() / sizeof(float) / 2;
|
size_t totalSamples = m_data->pcmData.size() / sizeof(float) / 2;
|
||||||
size_t sampleIdx = static_cast<size_t>(pos * totalSamples);
|
size_t sampleIdx = static_cast<size_t>(pos * totalSamples);
|
||||||
|
|
||||||
|
|
@ -661,52 +615,6 @@ void AudioAnalyzer::processLoop() {
|
||||||
m_deepProcessors[0]->pushData(complexL);
|
m_deepProcessors[0]->pushData(complexL);
|
||||||
m_deepProcessors[1]->pushData(complexR);
|
m_deepProcessors[1]->pushData(complexR);
|
||||||
|
|
||||||
#else
|
|
||||||
// Desktop path: random-access read from precomputed complex/PCM data
|
|
||||||
bool useComplex = !m_data->complexData.empty();
|
|
||||||
size_t totalSamples;
|
|
||||||
if (useComplex) {
|
|
||||||
totalSamples = m_data->complexData.size() / 2;
|
|
||||||
} else {
|
|
||||||
totalSamples = m_data->pcmData.size() / sizeof(float) / 2;
|
|
||||||
}
|
|
||||||
size_t sampleIdx = static_cast<size_t>(pos * totalSamples);
|
|
||||||
|
|
||||||
if (sampleIdx + m_frameSize >= totalSamples)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::vector<std::complex<double>> ch0(m_frameSize), ch1(m_frameSize);
|
|
||||||
if (useComplex) {
|
|
||||||
for (int i = 0; i < m_frameSize; ++i) {
|
|
||||||
ch0[i] = m_data->complexData[(sampleIdx + i) * 2];
|
|
||||||
ch1[i] = m_data->complexData[(sampleIdx + i) * 2 + 1];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const float *raw =
|
|
||||||
reinterpret_cast<const float *>(m_data->pcmData.constData());
|
|
||||||
for (int i = 0; i < m_frameSize; ++i) {
|
|
||||||
ch0[i] = std::complex<double>(raw[(sampleIdx + i) * 2], 0.0);
|
|
||||||
ch1[i] = std::complex<double>(raw[(sampleIdx + i) * 2 + 1], 0.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_processors[0]->pushData(ch0);
|
|
||||||
m_processors[1]->pushData(ch1);
|
|
||||||
|
|
||||||
int transSize = std::max(64, m_frameSize / 4);
|
|
||||||
std::vector<std::complex<double>> tCh0(transSize), tCh1(transSize);
|
|
||||||
int offset = m_frameSize - transSize;
|
|
||||||
for (int i = 0; i < transSize; ++i) {
|
|
||||||
tCh0[i] = ch0[offset + i];
|
|
||||||
tCh1[i] = ch1[offset + i];
|
|
||||||
}
|
|
||||||
m_transientProcessors[0]->pushData(tCh0);
|
|
||||||
m_transientProcessors[1]->pushData(tCh1);
|
|
||||||
|
|
||||||
m_deepProcessors[0]->pushData(ch0);
|
|
||||||
m_deepProcessors[1]->pushData(ch1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
computeAndPublishSpectrum();
|
computeAndPublishSpectrum();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// src/AudioEngine.h
|
// src/AudioEngine.h
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Processor.h"
|
#include "Processor.h"
|
||||||
#include "complex_block.h"
|
|
||||||
#include "complex_frames.h"
|
#include "complex_frames.h"
|
||||||
#include <QAudioDecoder>
|
#include <QAudioDecoder>
|
||||||
#include <QAudioSink>
|
#include <QAudioSink>
|
||||||
|
|
@ -20,7 +19,6 @@
|
||||||
// Shared Data Container (Thread-Safe via shared_ptr const correctness)
|
// Shared Data Container (Thread-Safe via shared_ptr const correctness)
|
||||||
struct TrackData {
|
struct TrackData {
|
||||||
QByteArray pcmData; // For playback
|
QByteArray pcmData; // For playback
|
||||||
std::vector<std::complex<double>> complexData; // For analysis
|
|
||||||
int sampleRate = 48000;
|
int sampleRate = 48000;
|
||||||
int frameSize = 4096;
|
int frameSize = 4096;
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
|
|
@ -248,7 +246,7 @@ private:
|
||||||
int m_frameSize = 4096;
|
int m_frameSize = 4096;
|
||||||
int m_hopSize = 1024;
|
int m_hopSize = 1024;
|
||||||
|
|
||||||
// RealtimeHilbert for mobile (streaming complex frames)
|
// RealtimeHilbert (streaming complex frames)
|
||||||
RealtimeHilbert m_hilbert;
|
RealtimeHilbert m_hilbert;
|
||||||
int m_hilbertFftSize = 8192;
|
int m_hilbertFftSize = 8192;
|
||||||
int m_hilbertHopSize = 1024;
|
int m_hilbertHopSize = 1024;
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,6 @@
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
|
||||||
#ifndef IS_MOBILE
|
|
||||||
#define IS_MOBILE
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PlaybackWidget::PlaybackWidget(QWidget *parent) : QWidget(parent) {
|
PlaybackWidget::PlaybackWidget(QWidget *parent) : QWidget(parent) {
|
||||||
setStyleSheet(
|
setStyleSheet(
|
||||||
"background-color: rgba(0, 0, 0, 150); border-top: 1px solid #444;");
|
"background-color: rgba(0, 0, 0, 150); border-top: 1px solid #444;");
|
||||||
|
|
@ -214,7 +208,6 @@ SettingsWidget::SettingsWidget(QWidget *parent) : QWidget(parent) {
|
||||||
QHBoxLayout *padsLayout = new QHBoxLayout();
|
QHBoxLayout *padsLayout = new QHBoxLayout();
|
||||||
|
|
||||||
m_padDsp = new XYPad("DSP", this);
|
m_padDsp = new XYPad("DSP", this);
|
||||||
#ifdef IS_MOBILE
|
|
||||||
m_padDsp->setFormatter([](float x, float y) {
|
m_padDsp->setFormatter([](float x, float y) {
|
||||||
int power = 6 + (int)(x * 7.0f + 0.5f);
|
int power = 6 + (int)(x * 7.0f + 0.5f);
|
||||||
int window = std::pow(2, power);
|
int window = std::pow(2, power);
|
||||||
|
|
@ -222,14 +215,6 @@ SettingsWidget::SettingsWidget(QWidget *parent) : QWidget(parent) {
|
||||||
if (hop > window) hop = window;
|
if (hop > window) hop = window;
|
||||||
return QString("Window: %1\nHop: %2").arg(window).arg(hop);
|
return QString("Window: %1\nHop: %2").arg(window).arg(hop);
|
||||||
});
|
});
|
||||||
#else
|
|
||||||
m_padDsp->setFormatter([](float x, float y) {
|
|
||||||
int power = 6 + (int)(x * 7.0f + 0.5f);
|
|
||||||
int fft = std::pow(2, power);
|
|
||||||
int hop = 64 + y * (8192 - 64);
|
|
||||||
return QString("FFT: %1\nHop: %2").arg(fft).arg(hop);
|
|
||||||
});
|
|
||||||
#endif
|
|
||||||
// Default to FFT 8192 (x=1.0), Hop 64 (y=0.0)
|
// Default to FFT 8192 (x=1.0), Hop 64 (y=0.0)
|
||||||
m_padDsp->setValues(1.0f, 0.0f);
|
m_padDsp->setValues(1.0f, 0.0f);
|
||||||
connect(m_padDsp, &XYPad::valuesChanged, this,
|
connect(m_padDsp, &XYPad::valuesChanged, this,
|
||||||
|
|
@ -330,9 +315,7 @@ void SettingsWidget::onDspPadChanged(float x, float y) {
|
||||||
int power = 6 + (int)(x * 7.0f + 0.5f);
|
int power = 6 + (int)(x * 7.0f + 0.5f);
|
||||||
m_fft = std::pow(2, power);
|
m_fft = std::pow(2, power);
|
||||||
m_hop = 64 + y * (8192 - 64);
|
m_hop = 64 + y * (8192 - 64);
|
||||||
#ifdef IS_MOBILE
|
|
||||||
if (m_hop > m_fft) m_hop = m_fft;
|
if (m_hop > m_fft) m_hop = m_fft;
|
||||||
#endif
|
|
||||||
emit dspParamsChanged(m_fft, m_hop);
|
emit dspParamsChanged(m_fft, m_hop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue