-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathAudioAnalyzer.cpp
More file actions
113 lines (98 loc) · 3.6 KB
/
Copy pathAudioAnalyzer.cpp
File metadata and controls
113 lines (98 loc) · 3.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// AudioAnalyzer.cpp
#include "AudioAnalyzer.h"
#include "Spectrum.h"
#include <QMediaDevices>
#include <QtMath>
#include <cstring>
AudioAnalyzer::AudioAnalyzer(QObject* parent) : QObject(parent) {
m_levels.reserve(m_numBands);
for (int i = 0; i < m_numBands; ++i) m_levels.append(0.0);
}
AudioAnalyzer::~AudioAnalyzer() { stop(); }
void AudioAnalyzer::start() {
if (m_active) return;
QAudioFormat fmt;
fmt.setSampleRate(m_sampleRate);
fmt.setChannelCount(1);
fmt.setSampleFormat(QAudioFormat::Float);
const QAudioDevice dev = QMediaDevices::defaultAudioInput();
if (dev.isNull()) {
qWarning("Nessun dispositivo di input audio trovato.");
return;
}
if (!dev.isFormatSupported(fmt)) {
fmt = dev.preferredFormat();
m_sampleRate = fmt.sampleRate();
}
m_format = fmt; // ricordo il formato effettivo per la decodifica
m_source = new QAudioSource(dev, fmt, this);
m_io = m_source->start(); // pull mode
if (!m_io) {
qWarning("Impossibile avviare la cattura audio.");
delete m_source; m_source = nullptr;
return;
}
connect(m_io, &QIODevice::readyRead, this, &AudioAnalyzer::onReadyRead);
m_active = true;
emit activeChanged();
}
void AudioAnalyzer::stop() {
if (m_source) { m_source->stop(); m_source->deleteLater(); m_source = nullptr; }
m_io = nullptr;
m_buffer.clear();
m_samples.clear();
if (m_active) { m_active = false; emit activeChanged(); }
}
// Converte un singolo campione (puntatore al primo byte) nel range float [-1,1],
// secondo il sampleFormat realmente concesso dal device.
static inline float sampleToFloat(const char* p, QAudioFormat::SampleFormat sf) {
switch (sf) {
case QAudioFormat::Float: {
float v; std::memcpy(&v, p, sizeof(float)); return v;
}
case QAudioFormat::Int16: {
qint16 v; std::memcpy(&v, p, sizeof(qint16));
return float(v) / 32768.0f;
}
case QAudioFormat::Int32: {
qint32 v; std::memcpy(&v, p, sizeof(qint32));
return float(double(v) / 2147483648.0);
}
case QAudioFormat::UInt8:
return (float(quint8(*p)) - 128.0f) / 128.0f;
default:
return 0.0f;
}
}
void AudioAnalyzer::onReadyRead() {
if (!m_io) return;
m_buffer.append(m_io->readAll());
const int bytesPerFrame = m_format.bytesPerFrame(); // campione * canali
const int channels = qMax(1, m_format.channelCount());
const int bytesPerSample = bytesPerFrame / channels;
if (bytesPerFrame <= 0) return;
// Accumulo finché non ho almeno fftSize frame.
const int avail = m_buffer.size() / bytesPerFrame; // frame disponibili
if (avail < int(m_fftSize)) return;
// Decodifico in mono float (media dei canali) secondo il formato effettivo.
const QAudioFormat::SampleFormat sf = m_format.sampleFormat();
const char* base = m_buffer.constData();
m_samples.resize(avail);
for (int f = 0; f < avail; ++f) {
const char* frame = base + qsizetype(f) * bytesPerFrame;
float acc = 0.0f;
for (int c = 0; c < channels; ++c)
acc += sampleToFloat(frame + c * bytesPerSample, sf);
m_samples[f] = acc / float(channels);
}
process();
// Consumo i frame elaborati (in byte).
m_buffer.remove(0, qsizetype(avail) * bytesPerFrame);
}
void AudioAnalyzer::process() {
auto r = dsp::analyze(m_samples, m_fftSize, m_numBands, m_sampleRate);
for (int i = 0; i < m_numBands && i < int(r.bands.size()); ++i)
m_levels[i] = qreal(r.bands[i]);
m_rms = qreal(r.rms);
emit updated();
}