Use nested namespaces (C++17 feature)

This commit is contained in:
Mat Sutcliffe
2021-09-15 21:44:54 +01:00
parent 3f2e5b0b69
commit 57d32da826
1345 changed files with 146075 additions and 150376 deletions

View File

@@ -8,46 +8,43 @@
#include "opusdecoder.h"
namespace BlackSound
namespace BlackSound::Codecs
{
namespace Codecs
COpusDecoder::COpusDecoder(int sampleRate, int channels) : m_channels(channels)
{
COpusDecoder::COpusDecoder(int sampleRate, int channels) : m_channels(channels)
{
int error;
m_opusDecoder = opus_decoder_create(sampleRate, channels, &error);
}
int error;
m_opusDecoder = opus_decoder_create(sampleRate, channels, &error);
}
COpusDecoder::~COpusDecoder()
{
opus_decoder_destroy(m_opusDecoder);
}
COpusDecoder::~COpusDecoder()
{
opus_decoder_destroy(m_opusDecoder);
}
int COpusDecoder::frameCount(int bufferSize)
{
// seems like bitrate should be required
int bitrate = 16;
int bytesPerSample = (bitrate / 8) * m_channels;
return bufferSize / bytesPerSample;
}
int COpusDecoder::frameCount(int bufferSize)
{
// seems like bitrate should be required
int bitrate = 16;
int bytesPerSample = (bitrate / 8) * m_channels;
return bufferSize / bytesPerSample;
}
QVector<qint16> COpusDecoder::decode(const QByteArray &opusData, int dataLength, int *decodedLength)
{
QVector<qint16> decoded(MaxDataBytes, 0);
int count = frameCount(MaxDataBytes);
QVector<qint16> COpusDecoder::decode(const QByteArray &opusData, int dataLength, int *decodedLength)
{
QVector<qint16> decoded(MaxDataBytes, 0);
int count = frameCount(MaxDataBytes);
if (!opusData.isEmpty())
{
*decodedLength = opus_decode(m_opusDecoder, reinterpret_cast<const unsigned char *>(opusData.data()), dataLength, decoded.data(), count, 0);
}
decoded.resize(*decodedLength);
return decoded;
}
void COpusDecoder::resetState()
if (!opusData.isEmpty())
{
if (!m_opusDecoder) { return; }
opus_decoder_ctl(m_opusDecoder, OPUS_RESET_STATE);
*decodedLength = opus_decode(m_opusDecoder, reinterpret_cast<const unsigned char *>(opusData.data()), dataLength, decoded.data(), count, 0);
}
} // ns
decoded.resize(*decodedLength);
return decoded;
}
void COpusDecoder::resetState()
{
if (!m_opusDecoder) { return; }
opus_decoder_ctl(m_opusDecoder, OPUS_RESET_STATE);
}
} // ns

View File

@@ -16,42 +16,39 @@
#include <QVector>
namespace BlackSound
namespace BlackSound::Codecs
{
namespace Codecs
//! OPUS decoder
class BLACKSOUND_EXPORT COpusDecoder
{
//! OPUS decoder
class BLACKSOUND_EXPORT COpusDecoder
{
public:
//! Ctor
COpusDecoder(int sampleRate, int channels);
public:
//! Ctor
COpusDecoder(int sampleRate, int channels);
//! Dtor
~COpusDecoder();
//! Dtor
~COpusDecoder();
//! Not copyable and assignable
//! @{
COpusDecoder(const COpusDecoder &decoder) = delete;
COpusDecoder& operator=(COpusDecoder const&) = delete;
//! @}
//! Not copyable and assignable
//! @{
COpusDecoder(const COpusDecoder &decoder) = delete;
COpusDecoder& operator=(COpusDecoder const&) = delete;
//! @}
//! Frame count
int frameCount(int bufferSize);
//! Frame count
int frameCount(int bufferSize);
//! Decode
QVector<qint16> decode(const QByteArray &opusData, int dataLength, int *decodedLength);
//! Decode
QVector<qint16> decode(const QByteArray &opusData, int dataLength, int *decodedLength);
//! Reset
void resetState();
//! Reset
void resetState();
private:
OpusDecoder *m_opusDecoder = nullptr;
int m_channels;
private:
OpusDecoder *m_opusDecoder = nullptr;
int m_channels;
static constexpr int MaxDataBytes = 4000;
};
} // ns
static constexpr int MaxDataBytes = 4000;
};
} // ns
#endif // guard

View File

@@ -8,33 +8,30 @@
#include "opusencoder.h"
namespace BlackSound
namespace BlackSound::Codecs
{
namespace Codecs
COpusEncoder::COpusEncoder(int sampleRate, int channels, int application)
{
COpusEncoder::COpusEncoder(int sampleRate, int channels, int application)
{
int error;
opusEncoder = opus_encoder_create(sampleRate, channels, application, &error);
}
int error;
opusEncoder = opus_encoder_create(sampleRate, channels, application, &error);
}
COpusEncoder::~COpusEncoder()
{
opus_encoder_destroy(opusEncoder);
}
COpusEncoder::~COpusEncoder()
{
opus_encoder_destroy(opusEncoder);
}
void COpusEncoder::setBitRate(int bitRate)
{
opus_encoder_ctl(opusEncoder, OPUS_SET_BITRATE(bitRate));
}
void COpusEncoder::setBitRate(int bitRate)
{
opus_encoder_ctl(opusEncoder, OPUS_SET_BITRATE(bitRate));
}
QByteArray COpusEncoder::encode(const QVector<qint16> &pcmSamples, int samplesLength, int *encodedLength)
{
QByteArray encoded(maxDataBytes, 0);
int length = opus_encode(opusEncoder, reinterpret_cast<const opus_int16 *>(pcmSamples.data()), samplesLength, reinterpret_cast<unsigned char *>(encoded.data()), maxDataBytes);
*encodedLength = length;
encoded.truncate(length);
return encoded;
}
} // ns
QByteArray COpusEncoder::encode(const QVector<qint16> &pcmSamples, int samplesLength, int *encodedLength)
{
QByteArray encoded(maxDataBytes, 0);
int length = opus_encode(opusEncoder, reinterpret_cast<const opus_int16 *>(pcmSamples.data()), samplesLength, reinterpret_cast<unsigned char *>(encoded.data()), maxDataBytes);
*encodedLength = length;
encoded.truncate(length);
return encoded;
}
} // ns

View File

@@ -17,38 +17,35 @@
#include <QByteArray>
#include <QVector>
namespace BlackSound
namespace BlackSound::Codecs
{
namespace Codecs
//! OPUS encoder
class BLACKSOUND_EXPORT COpusEncoder
{
//! OPUS encoder
class BLACKSOUND_EXPORT COpusEncoder
{
public:
//! Ctor
COpusEncoder(int sampleRate, int channels, int application = OPUS_APPLICATION_VOIP);
public:
//! Ctor
COpusEncoder(int sampleRate, int channels, int application = OPUS_APPLICATION_VOIP);
//! Dtor
~COpusEncoder();
//! Dtor
~COpusEncoder();
//! Non copyable
COpusEncoder(const COpusEncoder &temp_obj) = delete;
//! Non copyable
COpusEncoder(const COpusEncoder &temp_obj) = delete;
//! Non assignable
COpusEncoder &operator=(const COpusEncoder &temp_obj) = delete;
//! Non assignable
COpusEncoder &operator=(const COpusEncoder &temp_obj) = delete;
//! Bit rate
void setBitRate(int bitRate);
//! Bit rate
void setBitRate(int bitRate);
//! Encode
QByteArray encode(const QVector<qint16> &pcmSamples, int samplesLength, int *encodedLength);
//! Encode
QByteArray encode(const QVector<qint16> &pcmSamples, int samplesLength, int *encodedLength);
private:
OpusEncoder *opusEncoder = nullptr;
private:
OpusEncoder *opusEncoder = nullptr;
static constexpr int maxDataBytes = 4000;
};
} // ns
static constexpr int maxDataBytes = 4000;
};
} // ns
#endif // guard

View File

@@ -16,107 +16,104 @@
using namespace BlackMisc;
using namespace BlackConfig;
namespace BlackSound
namespace BlackSound::Dsp
{
namespace Dsp
float BiQuadFilter::transform(float inSample)
{
float BiQuadFilter::transform(float inSample)
{
// compute result
double result = m_a0 * inSample + m_a1 * m_x1 + m_a2 * m_x2 - m_a3 * m_y1 - m_a4 * m_y2;
// compute result
double result = m_a0 * inSample + m_a1 * m_x1 + m_a2 * m_x2 - m_a3 * m_y1 - m_a4 * m_y2;
// shift x1 to x2, sample to x1
m_x2 = m_x1;
m_x1 = inSample;
// shift x1 to x2, sample to x1
m_x2 = m_x1;
m_x1 = inSample;
// shift y1 to y2, result to y1
m_y2 = m_y1;
m_y1 = static_cast<float>(result);
// shift y1 to y2, result to y1
m_y2 = m_y1;
m_y1 = static_cast<float>(result);
return m_y1;
}
return m_y1;
}
void BiQuadFilter::setCoefficients(double aa0, double aa1, double aa2, double b0, double b1, double b2)
{
if (CBuildConfig::isLocalDeveloperDebugBuild()) { BLACK_VERIFY_X(qAbs(aa0) > 1E-06, Q_FUNC_INFO, "Div by zero?"); }
void BiQuadFilter::setCoefficients(double aa0, double aa1, double aa2, double b0, double b1, double b2)
{
if (CBuildConfig::isLocalDeveloperDebugBuild()) { BLACK_VERIFY_X(qAbs(aa0) > 1E-06, Q_FUNC_INFO, "Div by zero?"); }
// precompute the coefficients
m_a0 = b0 / aa0;
m_a1 = b1 / aa0;
m_a2 = b2 / aa0;
m_a3 = aa1 / aa0;
m_a4 = aa2 / aa0;
}
// precompute the coefficients
m_a0 = b0 / aa0;
m_a1 = b1 / aa0;
m_a2 = b2 / aa0;
m_a3 = aa1 / aa0;
m_a4 = aa2 / aa0;
}
void BiQuadFilter::setLowPassFilter(float sampleRate, float cutoffFrequency, float q)
{
// H(s) = 1 / (s^2 + s/Q + 1)
auto w0 = 2 * M_PI * cutoffFrequency / sampleRate;
auto cosw0 = qCos(w0);
auto alpha = qSin(w0) / (2 * q);
void BiQuadFilter::setLowPassFilter(float sampleRate, float cutoffFrequency, float q)
{
// H(s) = 1 / (s^2 + s/Q + 1)
auto w0 = 2 * M_PI * cutoffFrequency / sampleRate;
auto cosw0 = qCos(w0);
auto alpha = qSin(w0) / (2 * q);
auto b0 = (1 - cosw0) / 2;
auto b1 = 1 - cosw0;
auto b2 = (1 - cosw0) / 2;
auto aa0 = 1 + alpha;
auto aa1 = -2 * cosw0;
auto aa2 = 1 - alpha;
setCoefficients(aa0, aa1, aa2, b0, b1, b2);
}
auto b0 = (1 - cosw0) / 2;
auto b1 = 1 - cosw0;
auto b2 = (1 - cosw0) / 2;
auto aa0 = 1 + alpha;
auto aa1 = -2 * cosw0;
auto aa2 = 1 - alpha;
setCoefficients(aa0, aa1, aa2, b0, b1, b2);
}
void BiQuadFilter::setPeakingEq(float sampleRate, float centreFrequency, float q, float dbGain)
{
// H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
auto w0 = 2 * M_PI * centreFrequency / sampleRate;
auto cosw0 = qCos(w0);
auto sinw0 = qSin(w0);
auto alpha = sinw0 / (2 * q);
auto a = qPow(10, dbGain / 40); // TODO: should we square root this value?
void BiQuadFilter::setPeakingEq(float sampleRate, float centreFrequency, float q, float dbGain)
{
// H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
auto w0 = 2 * M_PI * centreFrequency / sampleRate;
auto cosw0 = qCos(w0);
auto sinw0 = qSin(w0);
auto alpha = sinw0 / (2 * q);
auto a = qPow(10, dbGain / 40); // TODO: should we square root this value?
auto b0 = 1 + alpha * a;
auto b1 = -2 * cosw0;
auto b2 = 1 - alpha * a;
auto aa0 = 1 + alpha / a;
auto aa1 = -2 * cosw0;
auto aa2 = 1 - alpha / a;
setCoefficients(aa0, aa1, aa2, b0, b1, b2);
}
auto b0 = 1 + alpha * a;
auto b1 = -2 * cosw0;
auto b2 = 1 - alpha * a;
auto aa0 = 1 + alpha / a;
auto aa1 = -2 * cosw0;
auto aa2 = 1 - alpha / a;
setCoefficients(aa0, aa1, aa2, b0, b1, b2);
}
void BiQuadFilter::setHighPassFilter(float sampleRate, float cutoffFrequency, float q)
{
// H(s) = s^2 / (s^2 + s/Q + 1)
auto w0 = 2 * M_PI * cutoffFrequency / sampleRate;
auto cosw0 = qCos(w0);
auto alpha = qSin(w0) / (2 * q);
void BiQuadFilter::setHighPassFilter(float sampleRate, float cutoffFrequency, float q)
{
// H(s) = s^2 / (s^2 + s/Q + 1)
auto w0 = 2 * M_PI * cutoffFrequency / sampleRate;
auto cosw0 = qCos(w0);
auto alpha = qSin(w0) / (2 * q);
auto b0 = (1 + cosw0) / 2;
auto b1 = -(1 + cosw0);
auto b2 = (1 + cosw0) / 2;
auto aa0 = 1 + alpha;
auto aa1 = -2 * cosw0;
auto aa2 = 1 - alpha;
setCoefficients(aa0, aa1, aa2, b0, b1, b2);
}
auto b0 = (1 + cosw0) / 2;
auto b1 = -(1 + cosw0);
auto b2 = (1 + cosw0) / 2;
auto aa0 = 1 + alpha;
auto aa1 = -2 * cosw0;
auto aa2 = 1 - alpha;
setCoefficients(aa0, aa1, aa2, b0, b1, b2);
}
BiQuadFilter BiQuadFilter::lowPassFilter(float sampleRate, float cutoffFrequency, float q)
{
BiQuadFilter filter;
filter.setLowPassFilter(sampleRate, cutoffFrequency, q);
return filter;
}
BiQuadFilter BiQuadFilter::lowPassFilter(float sampleRate, float cutoffFrequency, float q)
{
BiQuadFilter filter;
filter.setLowPassFilter(sampleRate, cutoffFrequency, q);
return filter;
}
BiQuadFilter BiQuadFilter::highPassFilter(float sampleRate, float cutoffFrequency, float q)
{
BiQuadFilter filter;
filter.setHighPassFilter(sampleRate, cutoffFrequency, q);
return filter;
}
BiQuadFilter BiQuadFilter::highPassFilter(float sampleRate, float cutoffFrequency, float q)
{
BiQuadFilter filter;
filter.setHighPassFilter(sampleRate, cutoffFrequency, q);
return filter;
}
BiQuadFilter BiQuadFilter::peakingEQ(float sampleRate, float centreFrequency, float q, float dbGain)
{
BiQuadFilter filter;
filter.setPeakingEq(sampleRate, centreFrequency, q, dbGain);
return filter;
}
} // ns
BiQuadFilter BiQuadFilter::peakingEQ(float sampleRate, float centreFrequency, float q, float dbGain)
{
BiQuadFilter filter;
filter.setPeakingEq(sampleRate, centreFrequency, q, dbGain);
return filter;
}
} // ns

View File

@@ -13,49 +13,46 @@
#include "blacksound/blacksoundexport.h"
namespace BlackSound
namespace BlackSound::Dsp
{
namespace Dsp
//! Digital biquad filter
class BiQuadFilter
{
//! Digital biquad filter
class BiQuadFilter
{
public:
//! Ctor
BiQuadFilter() = default;
public:
//! Ctor
BiQuadFilter() = default;
//! Transform
float transform(float inSample);
//! Transform
float transform(float inSample);
//! Set filter parameters
//! @{
void setCoefficients(double aa0, double aa1, double aa2, double b0, double b1, double b2);
void setLowPassFilter(float sampleRate, float cutoffFrequency, float q);
void setPeakingEq(float sampleRate, float centreFrequency, float q, float dbGain);
void setHighPassFilter(float sampleRate, float cutoffFrequency, float q);
//! @}
//! Set filter parameters
//! @{
void setCoefficients(double aa0, double aa1, double aa2, double b0, double b1, double b2);
void setLowPassFilter(float sampleRate, float cutoffFrequency, float q);
void setPeakingEq(float sampleRate, float centreFrequency, float q, float dbGain);
void setHighPassFilter(float sampleRate, float cutoffFrequency, float q);
//! @}
//! Get filters
//! @{
static BiQuadFilter lowPassFilter(float sampleRate, float cutoffFrequency, float q);
static BiQuadFilter highPassFilter(float sampleRate, float cutoffFrequency, float q);
static BiQuadFilter peakingEQ(float sampleRate, float centreFrequency, float q, float dbGain);
//! @}
//! Get filters
//! @{
static BiQuadFilter lowPassFilter(float sampleRate, float cutoffFrequency, float q);
static BiQuadFilter highPassFilter(float sampleRate, float cutoffFrequency, float q);
static BiQuadFilter peakingEQ(float sampleRate, float centreFrequency, float q, float dbGain);
//! @}
private:
double m_a0 = 0.0;
double m_a1 = 0.0;
double m_a2 = 0.0;
double m_a3 = 0.0;
double m_a4 = 0.0;
private:
double m_a0 = 0.0;
double m_a1 = 0.0;
double m_a2 = 0.0;
double m_a3 = 0.0;
double m_a4 = 0.0;
// state
float m_x1 = 0.0;
float m_x2 = 0.0;
float m_y1 = 0.0;
float m_y2 = 0.0;
};
} // ns
// state
float m_x1 = 0.0;
float m_x2 = 0.0;
float m_y1 = 0.0;
float m_y2 = 0.0;
};
} // ns
#endif // guard

View File

@@ -3,42 +3,39 @@
#include <QDebug>
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
CBufferedWaveProvider::CBufferedWaveProvider(const QAudioFormat &format, QObject *parent) :
ISampleProvider(parent)
{
CBufferedWaveProvider::CBufferedWaveProvider(const QAudioFormat &format, QObject *parent) :
ISampleProvider(parent)
{
const QString on = QStringLiteral("%1 format: '%2'").arg(this->metaObject()->className(), BlackSound::toQString(format));
this->setObjectName(on);
const QString on = QStringLiteral("%1 format: '%2'").arg(this->metaObject()->className(), BlackSound::toQString(format));
this->setObjectName(on);
// Set buffer size to 10 secs
m_maxBufferSize = format.bytesForDuration(10 * 1000 * 1000);
}
// Set buffer size to 10 secs
m_maxBufferSize = format.bytesForDuration(10 * 1000 * 1000);
}
void CBufferedWaveProvider::addSamples(const QVector<float> &samples)
void CBufferedWaveProvider::addSamples(const QVector<float> &samples)
{
int delta = m_audioBuffer.size() + samples.size() - m_maxBufferSize;
if (delta > 0)
{
int delta = m_audioBuffer.size() + samples.size() - m_maxBufferSize;
if (delta > 0)
{
m_audioBuffer.remove(0, delta);
}
m_audioBuffer.append(samples);
m_audioBuffer.remove(0, delta);
}
m_audioBuffer.append(samples);
}
int CBufferedWaveProvider::readSamples(QVector<float> &samples, qint64 count)
{
const int len = static_cast<int>(qMin(count, static_cast<qint64>(m_audioBuffer.size())));
samples = m_audioBuffer.mid(0, len);
// if (len != 0) qDebug() << "Reading" << count << "samples." << m_audioBuffer.size() << "currently in the buffer.";
m_audioBuffer.remove(0, len);
return len;
}
int CBufferedWaveProvider::readSamples(QVector<float> &samples, qint64 count)
{
const int len = static_cast<int>(qMin(count, static_cast<qint64>(m_audioBuffer.size())));
samples = m_audioBuffer.mid(0, len);
// if (len != 0) qDebug() << "Reading" << count << "samples." << m_audioBuffer.size() << "currently in the buffer.";
m_audioBuffer.remove(0, len);
return len;
}
void CBufferedWaveProvider::clearBuffer()
{
m_audioBuffer.clear();
}
} // ns
void CBufferedWaveProvider::clearBuffer()
{
m_audioBuffer.clear();
}
} // ns

View File

@@ -18,36 +18,33 @@
#include <QByteArray>
#include <QVector>
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
//! Buffered wave generator
class BLACKSOUND_EXPORT CBufferedWaveProvider : public ISampleProvider
{
//! Buffered wave generator
class BLACKSOUND_EXPORT CBufferedWaveProvider : public ISampleProvider
{
Q_OBJECT
Q_OBJECT
public:
//! Ctor
CBufferedWaveProvider(const QAudioFormat &format, QObject *parent = nullptr);
public:
//! Ctor
CBufferedWaveProvider(const QAudioFormat &format, QObject *parent = nullptr);
//! Add samples
void addSamples(const QVector<float> &samples);
//! Add samples
void addSamples(const QVector<float> &samples);
//! ISampleProvider::readSamples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! ISampleProvider::readSamples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! Bytes from buffer
int getBufferedBytes() const { return m_audioBuffer.size(); }
//! Bytes from buffer
int getBufferedBytes() const { return m_audioBuffer.size(); }
//! Clear the buffer
void clearBuffer();
//! Clear the buffer
void clearBuffer();
private:
QVector<float> m_audioBuffer;
qint32 m_maxBufferSize;
};
} // ns
private:
QVector<float> m_audioBuffer;
qint32 m_maxBufferSize;
};
} // ns
#endif // guard

View File

@@ -4,59 +4,56 @@
using namespace BlackSound::Dsp;
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
CEqualizerSampleProvider::CEqualizerSampleProvider(ISampleProvider *sourceProvider, EqualizerPresets preset, QObject *parent) :
ISampleProvider(parent)
{
CEqualizerSampleProvider::CEqualizerSampleProvider(ISampleProvider *sourceProvider, EqualizerPresets preset, QObject *parent) :
ISampleProvider(parent)
Q_ASSERT_X(sourceProvider, Q_FUNC_INFO, "Need provider");
const QString on = QStringLiteral("%1 of %2").arg(this->metaObject()->className(), sourceProvider->objectName());
this->setObjectName(on);
m_sourceProvider = sourceProvider;
setupPreset(preset);
}
int CEqualizerSampleProvider::readSamples(QVector<float> &samples, qint64 count)
{
const int samplesRead = m_sourceProvider->readSamples(samples, count);
if (m_bypass) return samplesRead;
for (int n = 0; n < samplesRead; n++)
{
Q_ASSERT_X(sourceProvider, Q_FUNC_INFO, "Need provider");
const QString on = QStringLiteral("%1 of %2").arg(this->metaObject()->className(), sourceProvider->objectName());
this->setObjectName(on);
m_sourceProvider = sourceProvider;
setupPreset(preset);
}
int CEqualizerSampleProvider::readSamples(QVector<float> &samples, qint64 count)
{
const int samplesRead = m_sourceProvider->readSamples(samples, count);
if (m_bypass) return samplesRead;
for (int n = 0; n < samplesRead; n++)
for (int band = 0; band < m_filters.size(); band++)
{
for (int band = 0; band < m_filters.size(); band++)
{
samples[n] = m_filters[band].transform(samples[n]);
}
samples[n] *= static_cast<float>(m_outputGain);
samples[n] = m_filters[band].transform(samples[n]);
}
return samplesRead;
samples[n] *= static_cast<float>(m_outputGain);
}
return samplesRead;
}
void CEqualizerSampleProvider::setupPreset(EqualizerPresets preset)
void CEqualizerSampleProvider::setupPreset(EqualizerPresets preset)
{
switch (preset)
{
switch (preset)
{
case VHFEmulation:
m_filters.push_back(BiQuadFilter::highPassFilter(44100, 310, 0.25));
m_filters.push_back(BiQuadFilter::peakingEQ(44100, 450, 0.75, 17.0));
m_filters.push_back(BiQuadFilter::peakingEQ(44100, 1450, 1.0, 25.0));
m_filters.push_back(BiQuadFilter::peakingEQ(44100, 2000, 1.0, 25.0));
m_filters.push_back(BiQuadFilter::lowPassFilter(44100, 2500, 0.25));
break;
}
case VHFEmulation:
m_filters.push_back(BiQuadFilter::highPassFilter(44100, 310, 0.25));
m_filters.push_back(BiQuadFilter::peakingEQ(44100, 450, 0.75, 17.0));
m_filters.push_back(BiQuadFilter::peakingEQ(44100, 1450, 1.0, 25.0));
m_filters.push_back(BiQuadFilter::peakingEQ(44100, 2000, 1.0, 25.0));
m_filters.push_back(BiQuadFilter::lowPassFilter(44100, 2500, 0.25));
break;
}
}
double CEqualizerSampleProvider::outputGain() const
{
return m_outputGain;
}
double CEqualizerSampleProvider::outputGain() const
{
return m_outputGain;
}
void CEqualizerSampleProvider::setOutputGain(double outputGain)
{
m_outputGain = outputGain;
}
} // ns
void CEqualizerSampleProvider::setOutputGain(double outputGain)
{
m_outputGain = outputGain;
}
} // ns

View File

@@ -18,47 +18,44 @@
#include <QSharedPointer>
#include <QVector>
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
//! Equalizer
enum EqualizerPresets
{
//! Equalizer
enum EqualizerPresets
{
VHFEmulation = 1
};
VHFEmulation = 1
};
//! Equalizer
class BLACKSOUND_EXPORT CEqualizerSampleProvider : public ISampleProvider
{
Q_OBJECT
//! Equalizer
class BLACKSOUND_EXPORT CEqualizerSampleProvider : public ISampleProvider
{
Q_OBJECT
public:
//! Ctor
CEqualizerSampleProvider(ISampleProvider *sourceProvider, EqualizerPresets preset, QObject *parent = nullptr);
public:
//! Ctor
CEqualizerSampleProvider(ISampleProvider *sourceProvider, EqualizerPresets preset, QObject *parent = nullptr);
//! \copydoc ISampleProvider::readSamples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! \copydoc ISampleProvider::readSamples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! Bypassing?
void setBypassEffects(bool value) { m_bypass = value; }
//! Bypassing?
void setBypassEffects(bool value) { m_bypass = value; }
//! Gain
//! @{
double outputGain() const;
void setOutputGain(double outputGain);
//! @}
//! Gain
//! @{
double outputGain() const;
void setOutputGain(double outputGain);
//! @}
private:
void setupPreset(EqualizerPresets preset);
private:
void setupPreset(EqualizerPresets preset);
ISampleProvider *m_sourceProvider = nullptr;
int m_channels = 1;
bool m_bypass = false;
double m_outputGain = 1.0;
QVector<Dsp::BiQuadFilter> m_filters;
};
} // ns
ISampleProvider *m_sourceProvider = nullptr;
int m_channels = 1;
bool m_bypass = false;
double m_outputGain = 1.0;
QVector<Dsp::BiQuadFilter> m_filters;
};
} // ns
#endif // guard

View File

@@ -11,57 +11,54 @@
using namespace BlackMisc;
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
CMixingSampleProvider::CMixingSampleProvider(QObject *parent) : ISampleProvider(parent)
{
CMixingSampleProvider::CMixingSampleProvider(QObject *parent) : ISampleProvider(parent)
const QString on = QStringLiteral("%1").arg(classNameShort(this));
this->setObjectName(on);
}
void CMixingSampleProvider::addMixerInput(ISampleProvider *provider)
{
Q_ASSERT(provider);
m_sources.append(provider);
const QString on = QStringLiteral("%1 sources: %2").arg(classNameShort(this)).arg(m_sources.size());
this->setObjectName(on);
}
int CMixingSampleProvider::readSamples(QVector<float> &samples, qint64 count)
{
samples.clear();
samples.fill(0, static_cast<int>(count));
int outputLen = 0;
QVector<ISampleProvider *> finishedProviders;
for (int i = 0; i < m_sources.size(); i++)
{
const QString on = QStringLiteral("%1").arg(classNameShort(this));
this->setObjectName(on);
}
ISampleProvider *sampleProvider = m_sources.at(i);
QVector<float> sourceBuffer;
void CMixingSampleProvider::addMixerInput(ISampleProvider *provider)
{
Q_ASSERT(provider);
m_sources.append(provider);
const QString on = QStringLiteral("%1 sources: %2").arg(classNameShort(this)).arg(m_sources.size());
this->setObjectName(on);
}
int CMixingSampleProvider::readSamples(QVector<float> &samples, qint64 count)
{
samples.clear();
samples.fill(0, static_cast<int>(count));
int outputLen = 0;
QVector<ISampleProvider *> finishedProviders;
for (int i = 0; i < m_sources.size(); i++)
const int len = sampleProvider->readSamples(sourceBuffer, count);
for (int n = 0; n < len; n++)
{
ISampleProvider *sampleProvider = m_sources.at(i);
QVector<float> sourceBuffer;
const int len = sampleProvider->readSamples(sourceBuffer, count);
for (int n = 0; n < len; n++)
{
samples[n] += sourceBuffer[n];
}
outputLen = qMax(len, outputLen);
if (sampleProvider->isFinished())
{
finishedProviders.push_back(sampleProvider);
}
samples[n] += sourceBuffer[n];
}
for (ISampleProvider *sampleProvider : finishedProviders)
outputLen = qMax(len, outputLen);
if (sampleProvider->isFinished())
{
sampleProvider->deleteLater();
m_sources.removeAll(sampleProvider);
finishedProviders.push_back(sampleProvider);
}
return outputLen;
}
} // ns
for (ISampleProvider *sampleProvider : finishedProviders)
{
sampleProvider->deleteLater();
m_sources.removeAll(sampleProvider);
}
return outputLen;
}
} // ns

View File

@@ -16,27 +16,24 @@
#include <QSharedPointer>
#include <QVector>
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
//! Mixer
class BLACKSOUND_EXPORT CMixingSampleProvider : public ISampleProvider
{
//! Mixer
class BLACKSOUND_EXPORT CMixingSampleProvider : public ISampleProvider
{
public:
//! Ctor mixing provider
CMixingSampleProvider(QObject *parent = nullptr);
public:
//! Ctor mixing provider
CMixingSampleProvider(QObject *parent = nullptr);
//! Add a provider
void addMixerInput(ISampleProvider *provider);
//! Add a provider
void addMixerInput(ISampleProvider *provider);
//! \copydoc ISampleProvider::readSamples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! \copydoc ISampleProvider::readSamples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
private:
QVector<ISampleProvider *> m_sources;
};
} // ns
private:
QVector<ISampleProvider *> m_sources;
};
} // ns
#endif // guard

View File

@@ -10,32 +10,29 @@
#include "pinknoisegenerator.h"
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
int CPinkNoiseGenerator::readSamples(QVector<float> &samples, qint64 count)
{
int CPinkNoiseGenerator::readSamples(QVector<float> &samples, qint64 count)
const int c = static_cast<int>(count);
samples.clear();
samples.fill(0, c);
for (int sampleCount = 0; sampleCount < count; sampleCount++)
{
const int c = static_cast<int>(count);
samples.clear();
samples.fill(0, c);
double white = 2 * m_random.generateDouble() - 1;
for (int sampleCount = 0; sampleCount < count; sampleCount++)
{
double white = 2 * m_random.generateDouble() - 1;
m_pinkNoiseBuffer[0] = 0.99886 * m_pinkNoiseBuffer[0] + white * 0.0555179;
m_pinkNoiseBuffer[1] = 0.99332 * m_pinkNoiseBuffer[1] + white * 0.0750759;
m_pinkNoiseBuffer[2] = 0.96900 * m_pinkNoiseBuffer[2] + white * 0.1538520;
m_pinkNoiseBuffer[3] = 0.86650 * m_pinkNoiseBuffer[3] + white * 0.3104856;
m_pinkNoiseBuffer[4] = 0.55000 * m_pinkNoiseBuffer[4] + white * 0.5329522;
m_pinkNoiseBuffer[5] = -0.7616 * m_pinkNoiseBuffer[5] - white * 0.0168980;
double pink = m_pinkNoiseBuffer[0] + m_pinkNoiseBuffer[1] + m_pinkNoiseBuffer[2] + m_pinkNoiseBuffer[3] + m_pinkNoiseBuffer[4] + m_pinkNoiseBuffer[5] + m_pinkNoiseBuffer[6] + white * 0.5362;
m_pinkNoiseBuffer[6] = white * 0.115926;
const float sampleValue = static_cast<float>(m_gain * (pink / 5));
samples[sampleCount] = sampleValue;
}
return c;
m_pinkNoiseBuffer[0] = 0.99886 * m_pinkNoiseBuffer[0] + white * 0.0555179;
m_pinkNoiseBuffer[1] = 0.99332 * m_pinkNoiseBuffer[1] + white * 0.0750759;
m_pinkNoiseBuffer[2] = 0.96900 * m_pinkNoiseBuffer[2] + white * 0.1538520;
m_pinkNoiseBuffer[3] = 0.86650 * m_pinkNoiseBuffer[3] + white * 0.3104856;
m_pinkNoiseBuffer[4] = 0.55000 * m_pinkNoiseBuffer[4] + white * 0.5329522;
m_pinkNoiseBuffer[5] = -0.7616 * m_pinkNoiseBuffer[5] - white * 0.0168980;
double pink = m_pinkNoiseBuffer[0] + m_pinkNoiseBuffer[1] + m_pinkNoiseBuffer[2] + m_pinkNoiseBuffer[3] + m_pinkNoiseBuffer[4] + m_pinkNoiseBuffer[5] + m_pinkNoiseBuffer[6] + white * 0.5362;
m_pinkNoiseBuffer[6] = white * 0.115926;
const float sampleValue = static_cast<float>(m_gain * (pink / 5));
samples[sampleCount] = sampleValue;
}
return c;
}
}

View File

@@ -19,31 +19,28 @@
#include <array>
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
//! Pink noise generator
class BLACKSOUND_EXPORT CPinkNoiseGenerator : public ISampleProvider
{
//! Pink noise generator
class BLACKSOUND_EXPORT CPinkNoiseGenerator : public ISampleProvider
{
Q_OBJECT
Q_OBJECT
public:
//! Noise generator
CPinkNoiseGenerator(QObject *parent = nullptr) : ISampleProvider(parent) {}
public:
//! Noise generator
CPinkNoiseGenerator(QObject *parent = nullptr) : ISampleProvider(parent) {}
//! Read samples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! Read samples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! Gain
void setGain(double gain) { m_gain = gain; }
//! Gain
void setGain(double gain) { m_gain = gain; }
private:
QRandomGenerator m_random;
std::array<double, 7> m_pinkNoiseBuffer = {{0}};
double m_gain = 0.0;
};
}
private:
QRandomGenerator m_random;
std::array<double, 7> m_pinkNoiseBuffer = {{0}};
double m_gain = 0.0;
};
}
#endif // guard

View File

@@ -17,53 +17,50 @@
using namespace BlackMisc;
using namespace BlackSound::Wav;
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
CResourceSound::CResourceSound() : m_data(new CResourceSoundData)
{
CResourceSound::CResourceSound() : m_data(new CResourceSoundData)
{
// void
}
// void
}
CResourceSound::CResourceSound(const QString &audioFileName) : m_data(new CResourceSoundData)
{
m_data->fileName = audioFileName;
}
CResourceSound::CResourceSound(const QString &audioFileName) : m_data(new CResourceSoundData)
{
m_data->fileName = audioFileName;
}
bool CResourceSound::load()
{
if (!m_data || m_data->fileName.isEmpty()) { return false; }
bool CResourceSound::load()
{
if (!m_data || m_data->fileName.isEmpty()) { return false; }
CWavFile wavFile;
m_data->samples.clear();
if (wavFile.open(m_data->fileName))
CWavFile wavFile;
m_data->samples.clear();
if (wavFile.open(m_data->fileName))
{
if (wavFile.fileFormat().sampleType() == QAudioFormat::Float)
{
if (wavFile.fileFormat().sampleType() == QAudioFormat::Float)
{
// Not implemented
// m_samples = convertFloatBytesTo16BitPCM(wavFile.audioData());
}
else
{
m_data->samples = convertBytesTo32BitFloatPCM(wavFile.audioData());
}
// Not implemented
// m_samples = convertFloatBytesTo16BitPCM(wavFile.audioData());
}
else
{
m_data->samples = convertBytesTo32BitFloatPCM(wavFile.audioData());
}
m_data->isLoaded = true;
return true;
}
const QString &CResourceSound::getFileName() const
{
static const QString empty;
return m_data ? m_data->fileName : empty;
}
m_data->isLoaded = true;
return true;
}
bool CResourceSound::isSameFileName(const QString &fn) const
{
if (fn.isEmpty()) { return false; }
return stringCompare(fn, m_data->fileName, CFileUtils::osFileNameCaseSensitivity());
}
} // ns
const QString &CResourceSound::getFileName() const
{
static const QString empty;
return m_data ? m_data->fileName : empty;
}
bool CResourceSound::isSameFileName(const QString &fn) const
{
if (fn.isEmpty()) { return false; }
return stringCompare(fn, m_data->fileName, CFileUtils::osFileNameCaseSensitivity());
}
} // ns

View File

@@ -20,47 +20,44 @@
#include <QExplicitlySharedDataPointer>
#include <atomic>
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
//! CResourceSound shared data
struct CResourceSoundData : public QSharedData
{
//! CResourceSound shared data
struct CResourceSoundData : public QSharedData
{
QString fileName; //!< file name
bool isLoaded = false; //!< is audio loaded
QVector<float> samples; //!< audio samples
};
QString fileName; //!< file name
bool isLoaded = false; //!< is audio loaded
QVector<float> samples; //!< audio samples
};
//! File from resources
class CResourceSound
{
public:
//! Constructor
CResourceSound();
//! File from resources
class CResourceSound
{
public:
//! Constructor
CResourceSound();
//! Sound of audio file
CResourceSound(const QString &audioFileName);
//! Sound of audio file
CResourceSound(const QString &audioFileName);
//! Load the attached resource file
bool load();
//! Load the attached resource file
bool load();
//! Is resource already loaded?
bool isLoaded() { return m_data->isLoaded; }
//! Is resource already loaded?
bool isLoaded() { return m_data->isLoaded; }
//! Audio data
const QVector<float> &audioData() const { return m_data->samples; }
//! Audio data
const QVector<float> &audioData() const { return m_data->samples; }
//! Corresponding file
const QString &getFileName() const;
//! Corresponding file
const QString &getFileName() const;
//! Is same file?
bool isSameFileName(const QString &fn) const;
//! Is same file?
bool isSameFileName(const QString &fn) const;
private:
QExplicitlySharedDataPointer<CResourceSoundData> m_data;
};
} // ns
private:
QExplicitlySharedDataPointer<CResourceSoundData> m_data;
};
} // ns
#endif // guard

View File

@@ -13,59 +13,56 @@
using namespace BlackMisc;
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
CResourceSoundSampleProvider::CResourceSoundSampleProvider(const CResourceSound &resourceSound, QObject *parent) :
ISampleProvider(parent),
m_resourceSound(resourceSound)
{
CResourceSoundSampleProvider::CResourceSoundSampleProvider(const CResourceSound &resourceSound, QObject *parent) :
ISampleProvider(parent),
m_resourceSound(resourceSound)
const QString on = QStringLiteral("%1 %2").arg(classNameShort(this), resourceSound.getFileName());
this->setObjectName(on);
m_tempBuffer.resize(m_tempBufferSize);
}
int CResourceSoundSampleProvider::readSamples(QVector<float> &samples, qint64 count)
{
if (!m_resourceSound.isLoaded()) { return 0; }
if (count > m_tempBufferSize)
{
const QString on = QStringLiteral("%1 %2").arg(classNameShort(this), resourceSound.getFileName());
this->setObjectName(on);
m_tempBuffer.resize(m_tempBufferSize);
qDebug() << "Count too large for temp buffer" << count;
return 0;
}
const qint64 availableSamples = m_resourceSound.audioData().size() - m_position;
const qint64 samplesToCopy = qMin(availableSamples, count);
samples.clear();
samples.fill(0, static_cast<int>(samplesToCopy));
for (int i = 0; i < samplesToCopy; i++)
{
m_tempBuffer[i] = m_resourceSound.audioData().at(static_cast<int>(m_position) + i);
}
int CResourceSoundSampleProvider::readSamples(QVector<float> &samples, qint64 count)
if (!qFuzzyCompare(m_gain, 1.0))
{
if (!m_resourceSound.isLoaded()) { return 0; }
if (count > m_tempBufferSize)
{
qDebug() << "Count too large for temp buffer" << count;
return 0;
}
const qint64 availableSamples = m_resourceSound.audioData().size() - m_position;
const qint64 samplesToCopy = qMin(availableSamples, count);
samples.clear();
samples.fill(0, static_cast<int>(samplesToCopy));
for (int i = 0; i < samplesToCopy; i++)
{
m_tempBuffer[i] = m_resourceSound.audioData().at(static_cast<int>(m_position) + i);
m_tempBuffer[i] = static_cast<float>(m_gain * m_tempBuffer[i]);
}
if (!qFuzzyCompare(m_gain, 1.0))
{
for (int i = 0; i < samplesToCopy; i++)
{
m_tempBuffer[i] = static_cast<float>(m_gain * m_tempBuffer[i]);
}
}
for (int i = 0; i < samplesToCopy; i++)
{
samples[i] = m_tempBuffer.at(i);
}
m_position += samplesToCopy;
if (m_position > availableSamples - 1)
{
if (m_looping) { m_position = 0; }
else { m_isFinished = true; }
}
return static_cast<int>(samplesToCopy);
}
} // ns
for (int i = 0; i < samplesToCopy; i++)
{
samples[i] = m_tempBuffer.at(i);
}
m_position += samplesToCopy;
if (m_position > availableSamples - 1)
{
if (m_looping) { m_position = 0; }
else { m_isFinished = true; }
}
return static_cast<int>(samplesToCopy);
}
} // ns

View File

@@ -13,48 +13,45 @@
#include "sampleprovider.h"
#include "resourcesound.h"
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
//! A sample provider
class BLACKSOUND_EXPORT CResourceSoundSampleProvider : public ISampleProvider
{
//! A sample provider
class BLACKSOUND_EXPORT CResourceSoundSampleProvider : public ISampleProvider
{
Q_OBJECT
Q_OBJECT
public:
//! Ctor
CResourceSoundSampleProvider(const CResourceSound &resourceSound, QObject *parent = nullptr);
public:
//! Ctor
CResourceSoundSampleProvider(const CResourceSound &resourceSound, QObject *parent = nullptr);
//! copydoc ISampleProvider::readSamples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! copydoc ISampleProvider::readSamples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! copydoc ISampleProvider::isFinished
virtual bool isFinished() const override { return m_isFinished; }
//! copydoc ISampleProvider::isFinished
virtual bool isFinished() const override { return m_isFinished; }
//! Looping
//! @{
bool looping() const { return m_looping; }
void setLooping(bool looping) { m_looping = looping; }
//! @}
//! Looping
//! @{
bool looping() const { return m_looping; }
void setLooping(bool looping) { m_looping = looping; }
//! @}
//! Gain
//! @{
double gain() const { return m_gain; }
void setGain(double gain) { m_gain = gain; }
//! @}
//! Gain
//! @{
double gain() const { return m_gain; }
void setGain(double gain) { m_gain = gain; }
//! @}
private:
double m_gain = 1.0;
bool m_looping = false;
private:
double m_gain = 1.0;
bool m_looping = false;
CResourceSound m_resourceSound;
qint64 m_position = 0;
const int m_tempBufferSize = 24000; //24000 = 500ms (avoid buffer overflow), m_tempBufferSize = 9600; //9600 = 200ms
QVector<float> m_tempBuffer;
bool m_isFinished = false;
};
} // ns
CResourceSound m_resourceSound;
qint64 m_position = 0;
const int m_tempBufferSize = 24000; //24000 = 500ms (avoid buffer overflow), m_tempBufferSize = 9600; //9600 = 200ms
QVector<float> m_tempBuffer;
bool m_isFinished = false;
};
} // ns
#endif // guard

View File

@@ -16,34 +16,31 @@
#include <QObject>
#include <QVector>
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
//! Sample provider interface
class BLACKSOUND_EXPORT ISampleProvider : public QObject
{
//! Sample provider interface
class BLACKSOUND_EXPORT ISampleProvider : public QObject
{
Q_OBJECT
Q_OBJECT
public:
//! Ctor
ISampleProvider(QObject *parent = nullptr) : QObject(parent) {}
public:
//! Ctor
ISampleProvider(QObject *parent = nullptr) : QObject(parent) {}
//! Dtor
virtual ~ISampleProvider() override {}
//! Dtor
virtual ~ISampleProvider() override {}
//! Read samples
virtual int readSamples(QVector<float> &samples, qint64 count) = 0;
//! Read samples
virtual int readSamples(QVector<float> &samples, qint64 count) = 0;
//! Finished?
virtual bool isFinished() const { return false; }
//! Finished?
virtual bool isFinished() const { return false; }
protected:
//! Verbose logs?
bool static verbose() { return BlackConfig::CBuildConfig::isLocalDeveloperDebugBuild(); }
};
protected:
//! Verbose logs?
bool static verbose() { return BlackConfig::CBuildConfig::isLocalDeveloperDebugBuild(); }
};
} // ns
} // ns
#endif // guard

View File

@@ -13,56 +13,53 @@
using namespace BlackMisc;
using namespace BlackMisc::Audio;
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
const Samples &Samples::instance()
{
const Samples &Samples::instance()
static const Samples samples;
return samples;
}
Samples::Samples()
{
this->initSounds();
}
void Samples::initSounds()
{
const CSettings settings = m_audioSettings.get();
QString f = settings.getNotificationFilePath(fnCrackle());
if (!m_crackle.isSameFileName(f))
{
static const Samples samples;
return samples;
m_crackle = CResourceSound(f);
m_crackle.load();
}
Samples::Samples()
f = settings.getNotificationFilePath(fnClick());
if (!m_click.isSameFileName(f))
{
this->initSounds();
m_click = CResourceSound(f);
m_click.load();
}
void Samples::initSounds()
f = settings.getNotificationFilePath(fnWhiteNoise());
if (!m_whiteNoise.isSameFileName(f))
{
const CSettings settings = m_audioSettings.get();
QString f = settings.getNotificationFilePath(fnCrackle());
if (!m_crackle.isSameFileName(f))
{
m_crackle = CResourceSound(f);
m_crackle.load();
}
f = settings.getNotificationFilePath(fnClick());
if (!m_click.isSameFileName(f))
{
m_click = CResourceSound(f);
m_click.load();
}
f = settings.getNotificationFilePath(fnWhiteNoise());
if (!m_whiteNoise.isSameFileName(f))
{
m_whiteNoise = CResourceSound(f);
m_whiteNoise.load();
}
f = settings.getNotificationFilePath(fnHfWhiteNoise());
if (!m_hfWhiteNoise.isSameFileName(f))
{
m_hfWhiteNoise = CResourceSound(f);
m_hfWhiteNoise.load();
}
m_whiteNoise = CResourceSound(f);
m_whiteNoise.load();
}
void Samples::onSettingsChanged()
f = settings.getNotificationFilePath(fnHfWhiteNoise());
if (!m_hfWhiteNoise.isSameFileName(f))
{
this->initSounds();
m_hfWhiteNoise = CResourceSound(f);
m_hfWhiteNoise.load();
}
} // ns
}
void Samples::onSettingsChanged()
{
this->initSounds();
}
} // ns

View File

@@ -18,58 +18,55 @@
#include <QObject>
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
//! Sound samples from resources (wav files)
class BLACKSOUND_EXPORT Samples : public QObject
{
//! Sound samples from resources (wav files)
class BLACKSOUND_EXPORT Samples : public QObject
{
public:
//! Singleton
static const Samples &instance();
public:
//! Singleton
static const Samples &instance();
//! Avoid to copy
Samples(const Samples &) = delete;
//! Avoid to copy
Samples(const Samples &) = delete;
//! Various samples (sounds)
//! @{
const CResourceSound &crackle() const { return m_crackle; }
const CResourceSound &click() const { return m_click; }
const CResourceSound &whiteNoise() const { return m_whiteNoise; }
const CResourceSound &hfWhiteNoise() const { return m_hfWhiteNoise; }
//! @}
//! Various samples (sounds)
//! @{
const CResourceSound &crackle() const { return m_crackle; }
const CResourceSound &click() const { return m_click; }
const CResourceSound &whiteNoise() const { return m_whiteNoise; }
const CResourceSound &hfWhiteNoise() const { return m_hfWhiteNoise; }
//! @}
//! Play the click sound
bool playClick() const { return m_audioSettings.get().pttClickUp(); }
//! Play the click sound
bool playClick() const { return m_audioSettings.get().pttClickUp(); }
//! File names
//! @{
static const QString &fnCrackle() { static const QString f = "afv_crackle_f32.wav"; return f; }
static const QString &fnClick() { static const QString f = "afv_click_f32.wav"; return f; }
static const QString &fnWhiteNoise() { static const QString f = "afv_whitenoise_f32.wav"; return f; }
static const QString &fnHfWhiteNoise() { static const QString f = "afv_hf_whiteNoise_f32.wav"; return f; }
//! @}
//! File names
//! @{
static const QString &fnCrackle() { static const QString f = "afv_crackle_f32.wav"; return f; }
static const QString &fnClick() { static const QString f = "afv_click_f32.wav"; return f; }
static const QString &fnWhiteNoise() { static const QString f = "afv_whitenoise_f32.wav"; return f; }
static const QString &fnHfWhiteNoise() { static const QString f = "afv_hf_whiteNoise_f32.wav"; return f; }
//! @}
private:
//! Ctor
Samples();
private:
//! Ctor
Samples();
CResourceSound m_crackle;
CResourceSound m_click;
CResourceSound m_whiteNoise;
CResourceSound m_hfWhiteNoise;
CResourceSound m_crackle;
CResourceSound m_click;
CResourceSound m_whiteNoise;
CResourceSound m_hfWhiteNoise;
BlackMisc::CSetting<BlackMisc::Audio::TSettings> m_audioSettings { this, &Samples::onSettingsChanged };
BlackMisc::CSetting<BlackMisc::Audio::TSettings> m_audioSettings { this, &Samples::onSettingsChanged };
//! Init sounds
void initSounds();
//! Init sounds
void initSounds();
//! Settings have been changed
void onSettingsChanged();
};
//! Settings have been changed
void onSettingsChanged();
};
} // ns
} // ns
#endif // guard

View File

@@ -9,31 +9,28 @@
#include "sawtoothgenerator.h"
#include <cmath>
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
CSawToothGenerator::CSawToothGenerator(double frequency, QObject *parent) :
ISampleProvider(parent),
m_frequency(frequency)
{
CSawToothGenerator::CSawToothGenerator(double frequency, QObject *parent) :
ISampleProvider(parent),
m_frequency(frequency)
{
this->setObjectName("CSawToothGenerator");
}
this->setObjectName("CSawToothGenerator");
}
int CSawToothGenerator::readSamples(QVector<float> &samples, qint64 count)
{
samples.clear();
samples.fill(0, static_cast<int>(count));
int CSawToothGenerator::readSamples(QVector<float> &samples, qint64 count)
{
samples.clear();
samples.fill(0, static_cast<int>(count));
for (int sampleCount = 0; sampleCount < count; sampleCount++)
{
double multiple = 2 * m_frequency / m_sampleRate;
double sampleSaw = std::fmod((m_nSample * multiple), 2) - 1;
double sampleValue = m_gain * sampleSaw;
samples[sampleCount] = static_cast<float>(sampleValue);
m_nSample++;
}
return static_cast<int>(count);
for (int sampleCount = 0; sampleCount < count; sampleCount++)
{
double multiple = 2 * m_frequency / m_sampleRate;
double sampleSaw = std::fmod((m_nSample * multiple), 2) - 1;
double sampleValue = m_gain * sampleSaw;
samples[sampleCount] = static_cast<float>(sampleValue);
m_nSample++;
}
} // ns
return static_cast<int>(count);
}
} // ns

View File

@@ -17,32 +17,29 @@
#include <QVector>
#include <array>
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
//! Saw tooth generator
class BLACKSOUND_EXPORT CSawToothGenerator : public ISampleProvider
{
//! Saw tooth generator
class BLACKSOUND_EXPORT CSawToothGenerator : public ISampleProvider
{
Q_OBJECT
Q_OBJECT
public:
//! Ctor
CSawToothGenerator(double frequency, QObject *parent = nullptr);
public:
//! Ctor
CSawToothGenerator(double frequency, QObject *parent = nullptr);
//! \copydoc ISampleProvider::readSamples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! \copydoc ISampleProvider::readSamples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! Set the gain
void setGain(double gain) { m_gain = gain; }
//! Set the gain
void setGain(double gain) { m_gain = gain; }
private:
double m_gain = 0.0;
double m_frequency = 0.0;
double m_sampleRate = 48000;
int m_nSample = 0;
};
} // ns
private:
double m_gain = 0.0;
double m_frequency = 0.0;
double m_sampleRate = 48000;
int m_nSample = 0;
};
} // ns
#endif // guard

View File

@@ -9,64 +9,61 @@
#include "simplecompressoreffect.h"
#include <QDebug>
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
CSimpleCompressorEffect::CSimpleCompressorEffect(ISampleProvider *source, QObject *parent) :
ISampleProvider(parent),
m_sourceStream(source)
{
CSimpleCompressorEffect::CSimpleCompressorEffect(ISampleProvider *source, QObject *parent) :
ISampleProvider(parent),
m_sourceStream(source)
this->setObjectName("CSimpleCompressorEffect");
m_timer = new QTimer(this);
m_timer->setObjectName(this->objectName() + ":m_timer");
m_simpleCompressor.setAttack(5.0);
m_simpleCompressor.setRelease(10.0);
m_simpleCompressor.setSampleRate(48000.0);
m_simpleCompressor.setThresh(16.0);
m_simpleCompressor.setRatio(6.0);
m_simpleCompressor.setMakeUpGain(16.0);
m_timer->start(3000);
}
int CSimpleCompressorEffect::readSamples(QVector<float> &samples, qint64 count)
{
int samplesRead = m_sourceStream->readSamples(samples, count);
if (m_enabled)
{
this->setObjectName("CSimpleCompressorEffect");
m_timer = new QTimer(this);
m_timer->setObjectName(this->objectName() + ":m_timer");
m_simpleCompressor.setAttack(5.0);
m_simpleCompressor.setRelease(10.0);
m_simpleCompressor.setSampleRate(48000.0);
m_simpleCompressor.setThresh(16.0);
m_simpleCompressor.setRatio(6.0);
m_simpleCompressor.setMakeUpGain(16.0);
m_timer->start(3000);
}
int CSimpleCompressorEffect::readSamples(QVector<float> &samples, qint64 count)
{
int samplesRead = m_sourceStream->readSamples(samples, count);
if (m_enabled)
for (int sample = 0; sample < samplesRead; sample += m_channels)
{
for (int sample = 0; sample < samplesRead; sample += m_channels)
double in1 = samples.at(sample);
double in2 = (m_channels == 1) ? 0 : samples.at(sample + 1);
m_simpleCompressor.process(in1, in2);
samples[sample] = static_cast<float>(in1);
if (m_channels > 1)
{
double in1 = samples.at(sample);
double in2 = (m_channels == 1) ? 0 : samples.at(sample + 1);
m_simpleCompressor.process(in1, in2);
samples[sample] = static_cast<float>(in1);
if (m_channels > 1)
{
samples[sample + 1] = static_cast<float>(in2);
}
samples[sample + 1] = static_cast<float>(in2);
}
}
return samplesRead;
}
return samplesRead;
}
void CSimpleCompressorEffect::setEnabled(bool enabled)
{
m_enabled = enabled;
}
void CSimpleCompressorEffect::setEnabled(bool enabled)
{
m_enabled = enabled;
}
void CSimpleCompressorEffect::setMakeUpGain(double gain)
{
m_simpleCompressor.setMakeUpGain(gain);
}
void CSimpleCompressorEffect::setMakeUpGain(double gain)
{
m_simpleCompressor.setMakeUpGain(gain);
}
void CSimpleCompressorEffect::setChannels(int channels)
{
if (channels < 1) { channels = 1; }
else if (channels > 2) { channels = 2; }
m_channels = channels;
}
void CSimpleCompressorEffect::setChannels(int channels)
{
if (channels < 1) { channels = 1; }
else if (channels > 2) { channels = 2; }
m_channels = channels;
}
}

View File

@@ -18,39 +18,36 @@
#include <QObject>
#include <QTimer>
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
//! Compressor effect
class BLACKSOUND_EXPORT CSimpleCompressorEffect : public ISampleProvider
{
//! Compressor effect
class BLACKSOUND_EXPORT CSimpleCompressorEffect : public ISampleProvider
{
Q_OBJECT
Q_OBJECT
public:
//! Ctor
CSimpleCompressorEffect(ISampleProvider *source, QObject *parent = nullptr);
public:
//! Ctor
CSimpleCompressorEffect(ISampleProvider *source, QObject *parent = nullptr);
//! \copydoc ISampleProvider::readSamples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! \copydoc ISampleProvider::readSamples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! Enable
void setEnabled(bool enabled);
//! Enable
void setEnabled(bool enabled);
//! Set gain
void setMakeUpGain(double gain);
//! Set gain
void setMakeUpGain(double gain);
//! Set channels 1 or 2
void setChannels(int channels);
//! Set channels 1 or 2
void setChannels(int channels);
private:
QTimer *m_timer = nullptr;
ISampleProvider *m_sourceStream = nullptr;
bool m_enabled = true;
int m_channels = 1;
chunkware_simple::SimpleComp m_simpleCompressor;
};
} // ns
private:
QTimer *m_timer = nullptr;
ISampleProvider *m_sourceStream = nullptr;
bool m_enabled = true;
int m_channels = 1;
chunkware_simple::SimpleComp m_simpleCompressor;
};
} // ns
#endif // guard

View File

@@ -12,36 +12,33 @@
using namespace BlackMisc;
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
CSinusGenerator::CSinusGenerator(double frequencyHz, QObject *parent) :
ISampleProvider(parent),
m_frequencyHz(frequencyHz)
{
CSinusGenerator::CSinusGenerator(double frequencyHz, QObject *parent) :
ISampleProvider(parent),
m_frequencyHz(frequencyHz)
{
const QString on = QStringLiteral("%1 frequency: %2Hz").arg(classNameShort(this)).arg(frequencyHz);
this->setObjectName(on);
}
const QString on = QStringLiteral("%1 frequency: %2Hz").arg(classNameShort(this)).arg(frequencyHz);
this->setObjectName(on);
}
int CSinusGenerator::readSamples(QVector<float> &samples, qint64 count)
{
samples.clear();
samples.fill(0, static_cast<int>(count));
int CSinusGenerator::readSamples(QVector<float> &samples, qint64 count)
{
samples.clear();
samples.fill(0, static_cast<int>(count));
for (int sampleCount = 0; sampleCount < count; sampleCount++)
{
const double multiple = s_twoPi * m_frequencyHz / m_sampleRate;
const double sampleValue = m_gain * qSin(m_nSample * multiple);
samples[sampleCount] = static_cast<float>(sampleValue);
m_nSample++;
}
return static_cast<int>(count);
}
void CSinusGenerator::setFrequency(double frequencyHz)
for (int sampleCount = 0; sampleCount < count; sampleCount++)
{
m_frequencyHz = frequencyHz;
const double multiple = s_twoPi * m_frequencyHz / m_sampleRate;
const double sampleValue = m_gain * qSin(m_nSample * multiple);
samples[sampleCount] = static_cast<float>(sampleValue);
m_nSample++;
}
} // ns
return static_cast<int>(count);
}
void CSinusGenerator::setFrequency(double frequencyHz)
{
m_frequencyHz = frequencyHz;
}
} // ns

View File

@@ -16,36 +16,33 @@
#include <QtMath>
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
//! Saw tooth generator
class BLACKSOUND_EXPORT CSinusGenerator : public ISampleProvider
{
//! Saw tooth generator
class BLACKSOUND_EXPORT CSinusGenerator : public ISampleProvider
{
Q_OBJECT
Q_OBJECT
public:
//! Ctor
CSinusGenerator(double frequencyHz, QObject *parent = nullptr);
public:
//! Ctor
CSinusGenerator(double frequencyHz, QObject *parent = nullptr);
//! \copydoc ISampleProvider::readSamples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! \copydoc ISampleProvider::readSamples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! Set the gain
void setGain(double gain) { m_gain = gain; }
//! Set the gain
void setGain(double gain) { m_gain = gain; }
//! Set frequency in Hz
void setFrequency(double frequencyHz);
//! Set frequency in Hz
void setFrequency(double frequencyHz);
private:
double m_gain = 0.0;
double m_frequencyHz = 0.0;
double m_sampleRate = 48000;
int m_nSample = 0;
static constexpr double s_twoPi = 2 * M_PI;
};
} // ns
private:
double m_gain = 0.0;
double m_frequencyHz = 0.0;
double m_sampleRate = 48000;
int m_nSample = 0;
static constexpr double s_twoPi = 2 * M_PI;
};
} // ns
#endif // guard

View File

@@ -13,37 +13,34 @@
using namespace BlackMisc;
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
CVolumeSampleProvider::CVolumeSampleProvider(ISampleProvider *sourceProvider, QObject *parent) :
ISampleProvider(parent),
m_sourceProvider(sourceProvider)
{
CVolumeSampleProvider::CVolumeSampleProvider(ISampleProvider *sourceProvider, QObject *parent) :
ISampleProvider(parent),
m_sourceProvider(sourceProvider)
{
Q_ASSERT_X(sourceProvider, Q_FUNC_INFO, "Need source provider");
const QString on = QStringLiteral("%1 with source: %2").arg(classNameShort(this), sourceProvider->objectName());
this->setObjectName(on);
}
Q_ASSERT_X(sourceProvider, Q_FUNC_INFO, "Need source provider");
const QString on = QStringLiteral("%1 with source: %2").arg(classNameShort(this), sourceProvider->objectName());
this->setObjectName(on);
}
int CVolumeSampleProvider::readSamples(QVector<float> &samples, qint64 count)
int CVolumeSampleProvider::readSamples(QVector<float> &samples, qint64 count)
{
const int samplesRead = m_sourceProvider->readSamples(samples, count);
if (!qFuzzyCompare(m_gainRatio, 1.0))
{
const int samplesRead = m_sourceProvider->readSamples(samples, count);
if (!qFuzzyCompare(m_gainRatio, 1.0))
for (int n = 0; n < samplesRead; n++)
{
for (int n = 0; n < samplesRead; n++)
{
samples[n] = static_cast<float>(m_gainRatio * samples[n]);
}
samples[n] = static_cast<float>(m_gainRatio * samples[n]);
}
return samplesRead;
}
return samplesRead;
}
bool CVolumeSampleProvider::setGainRatio(double volume)
{
const bool changed = !qFuzzyCompare(m_gainRatio, volume);
if (changed) { m_gainRatio = volume; }
return changed;
}
} // ns
bool CVolumeSampleProvider::setGainRatio(double volume)
{
const bool changed = !qFuzzyCompare(m_gainRatio, volume);
if (changed) { m_gainRatio = volume; }
return changed;
}
} // ns

View File

@@ -14,38 +14,35 @@
#include "blacksound/blacksoundexport.h"
#include "blacksound/sampleprovider/sampleprovider.h"
namespace BlackSound
namespace BlackSound::SampleProvider
{
namespace SampleProvider
//! Pink noise generator
class BLACKSOUND_EXPORT CVolumeSampleProvider : public ISampleProvider
{
//! Pink noise generator
class BLACKSOUND_EXPORT CVolumeSampleProvider : public ISampleProvider
{
Q_OBJECT
Q_OBJECT
public:
//! Noise generator
CVolumeSampleProvider(ISampleProvider *sourceProvider, QObject *parent = nullptr);
public:
//! Noise generator
CVolumeSampleProvider(ISampleProvider *sourceProvider, QObject *parent = nullptr);
//! \copydoc ISampleProvider::readSamples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! \copydoc ISampleProvider::readSamples
virtual int readSamples(QVector<float> &samples, qint64 count) override;
//! Gain ratio, value a amplitude need to be multiplied with
//! \see http://www.sengpielaudio.com/calculator-amplification.htm
//! \remark gain ratio is voltage ratio/or amplitude ratio, something between 0.001-7.95 for -60dB to 80dB
//! @{
double getGainRatio() const { return m_gainRatio; }
bool setGainRatio(double gainRatio);
//! @}
// those used to be the original function names
// double volume() const { return m_volume; }
// bool setVolume(double volume);
//! Gain ratio, value a amplitude need to be multiplied with
//! \see http://www.sengpielaudio.com/calculator-amplification.htm
//! \remark gain ratio is voltage ratio/or amplitude ratio, something between 0.001-7.95 for -60dB to 80dB
//! @{
double getGainRatio() const { return m_gainRatio; }
bool setGainRatio(double gainRatio);
//! @}
// those used to be the original function names
// double volume() const { return m_volume; }
// bool setVolume(double volume);
private:
ISampleProvider *m_sourceProvider = nullptr;
double m_gainRatio = 1.0;
};
} // ns
private:
ISampleProvider *m_sourceProvider = nullptr;
double m_gainRatio = 1.0;
};
} // ns
#endif // guard

View File

@@ -12,140 +12,137 @@
#include "wavfile.h"
// #include "utils.h"
namespace BlackSound
namespace BlackSound::Wav
{
namespace Wav
//! WAV chunk
struct chunk
{
//! WAV chunk
struct chunk
{
char id[4]; //!< chunk id
quint32 size; //!< chunk size
};
char id[4]; //!< chunk id
quint32 size; //!< chunk size
};
//! RIFF header
struct RIFFHeader
{
chunk descriptor; //!< "RIFF"
char type[4]; //!< "WAVE"
};
//! RIFF header
struct RIFFHeader
{
chunk descriptor; //!< "RIFF"
char type[4]; //!< "WAVE"
};
//! WAVE header
struct WAVEHeader
{
chunk descriptor; //!< chunk descriptor
quint16 audioFormat; //!< audio format, e.g. 0x0001 => PCM
quint16 numChannels; //!< number of channels
quint32 sampleRate; //!< sample rate
quint32 byteRate; //!< byte rate
quint16 blockAlign; //!< block align
quint16 bitsPerSample; //!< bits per sample
};
//! WAVE header
struct WAVEHeader
{
chunk descriptor; //!< chunk descriptor
quint16 audioFormat; //!< audio format, e.g. 0x0001 => PCM
quint16 numChannels; //!< number of channels
quint32 sampleRate; //!< sample rate
quint32 byteRate; //!< byte rate
quint16 blockAlign; //!< block align
quint16 bitsPerSample; //!< bits per sample
};
//! Data header
struct DATAHeader
{
chunk descriptor; //!< chunk descriptor
};
//! Data header
struct DATAHeader
{
chunk descriptor; //!< chunk descriptor
};
//! Combined header
struct CombinedHeader
{
RIFFHeader riff; //!< RIFF header
WAVEHeader wave; //!< WAVE header
};
//! Combined header
struct CombinedHeader
{
RIFFHeader riff; //!< RIFF header
WAVEHeader wave; //!< WAVE header
};
CWavFile::CWavFile(QObject *parent) :
QFile(parent),
m_headerLength(0)
{ }
CWavFile::CWavFile(QObject *parent) :
QFile(parent),
m_headerLength(0)
{ }
bool CWavFile::open(const QString &fileName)
{
this->close();
this->setFileName(fileName);
return QFile::open(QIODevice::ReadOnly) && readHeader();
}
bool CWavFile::open(const QString &fileName)
{
this->close();
this->setFileName(fileName);
return QFile::open(QIODevice::ReadOnly) && readHeader();
}
const QAudioFormat &CWavFile::fileFormat() const
{
return m_fileFormat;
}
const QAudioFormat &CWavFile::fileFormat() const
{
return m_fileFormat;
}
qint64 CWavFile::headerLength() const
{
return m_headerLength;
}
qint64 CWavFile::headerLength() const
{
return m_headerLength;
}
bool CWavFile::readHeader()
bool CWavFile::readHeader()
{
seek(0);
CombinedHeader header;
DATAHeader dataHeader;
bool result = read(reinterpret_cast<char *>(&header), sizeof(CombinedHeader)) == sizeof(CombinedHeader);
if (result)
{
seek(0);
CombinedHeader header;
DATAHeader dataHeader;
bool result = read(reinterpret_cast<char *>(&header), sizeof(CombinedHeader)) == sizeof(CombinedHeader);
if (result)
if ((memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0
|| memcmp(&header.riff.descriptor.id, "RIFX", 4) == 0)
&& memcmp(&header.riff.type, "WAVE", 4) == 0
&& memcmp(&header.wave.descriptor.id, "fmt ", 4) == 0
&& (header.wave.audioFormat == 1 || header.wave.audioFormat == 0 || header.wave.audioFormat == 3))
{
if ((memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0
|| memcmp(&header.riff.descriptor.id, "RIFX", 4) == 0)
&& memcmp(&header.riff.type, "WAVE", 4) == 0
&& memcmp(&header.wave.descriptor.id, "fmt ", 4) == 0
&& (header.wave.audioFormat == 1 || header.wave.audioFormat == 0 || header.wave.audioFormat == 3))
// Read off remaining header information
if (qFromLittleEndian<quint32>(header.wave.descriptor.size) > sizeof(WAVEHeader))
{
// Read off remaining header information
if (qFromLittleEndian<quint32>(header.wave.descriptor.size) > sizeof(WAVEHeader))
{
// Extended data available
quint16 extraFormatBytes;
if (peek((char *)&extraFormatBytes, sizeof(quint16)) != sizeof(quint16))
return false;
const qint64 throwAwayBytes = sizeof(quint16) + qFromLittleEndian<quint16>(extraFormatBytes);
if (read(throwAwayBytes).size() != throwAwayBytes)
return false;
}
if (read((char *)&dataHeader, sizeof(DATAHeader)) != sizeof(DATAHeader))
// Extended data available
quint16 extraFormatBytes;
if (peek((char *)&extraFormatBytes, sizeof(quint16)) != sizeof(quint16))
return false;
const qint64 throwAwayBytes = sizeof(quint16) + qFromLittleEndian<quint16>(extraFormatBytes);
if (read(throwAwayBytes).size() != throwAwayBytes)
return false;
}
// Establish format
if (memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0)
m_fileFormat.setByteOrder(QAudioFormat::LittleEndian);
else
m_fileFormat.setByteOrder(QAudioFormat::BigEndian);
if (read((char *)&dataHeader, sizeof(DATAHeader)) != sizeof(DATAHeader))
return false;
int bps = qFromLittleEndian<quint16>(header.wave.bitsPerSample);
m_fileFormat.setChannelCount(qFromLittleEndian<quint16>(header.wave.numChannels));
m_fileFormat.setCodec("audio/pcm");
m_fileFormat.setSampleRate(qFromLittleEndian<quint32>(header.wave.sampleRate));
m_fileFormat.setSampleSize(qFromLittleEndian<quint16>(header.wave.bitsPerSample));
// Establish format
if (memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0)
m_fileFormat.setByteOrder(QAudioFormat::LittleEndian);
else
m_fileFormat.setByteOrder(QAudioFormat::BigEndian);
if (header.wave.audioFormat == 1 || header.wave.audioFormat == 0)
{
m_fileFormat.setSampleType(bps == 8 ? QAudioFormat::UnSignedInt : QAudioFormat::SignedInt);
}
else
{
m_fileFormat.setSampleType(QAudioFormat::Float);
}
int bps = qFromLittleEndian<quint16>(header.wave.bitsPerSample);
m_fileFormat.setChannelCount(qFromLittleEndian<quint16>(header.wave.numChannels));
m_fileFormat.setCodec("audio/pcm");
m_fileFormat.setSampleRate(qFromLittleEndian<quint32>(header.wave.sampleRate));
m_fileFormat.setSampleSize(qFromLittleEndian<quint16>(header.wave.bitsPerSample));
if (header.wave.audioFormat == 1 || header.wave.audioFormat == 0)
{
m_fileFormat.setSampleType(bps == 8 ? QAudioFormat::UnSignedInt : QAudioFormat::SignedInt);
}
else
{
result = false;
m_fileFormat.setSampleType(QAudioFormat::Float);
}
}
m_headerLength = pos();
if (memcmp(&dataHeader.descriptor.id, "data", 4) == 0)
{
const qint32 dataLength = qFromLittleEndian<qint32>(dataHeader.descriptor.size);
m_audioData = read(dataLength);
if (m_audioData.size() != dataLength)
{
m_audioData.clear();
return false;
}
}
return result;
else
{
result = false;
}
}
} // ns
m_headerLength = pos();
if (memcmp(&dataHeader.descriptor.id, "data", 4) == 0)
{
const qint32 dataLength = qFromLittleEndian<qint32>(dataHeader.descriptor.size);
m_audioData = read(dataLength);
if (m_audioData.size() != dataLength)
{
m_audioData.clear();
return false;
}
}
return result;
}
} // ns

View File

@@ -15,40 +15,37 @@
#include <QFile>
#include <QAudioFormat>
namespace BlackSound
namespace BlackSound::Wav
{
namespace Wav
//! * WAV file
class CWavFile : public QFile
{
//! * WAV file
class CWavFile : public QFile
{
public:
//! Ctor
CWavFile(QObject *parent = nullptr);
public:
//! Ctor
CWavFile(QObject *parent = nullptr);
//! Standard open
using QFile::open;
//! Standard open
using QFile::open;
//! Open
bool open(const QString &fileName);
//! Open
bool open(const QString &fileName);
//! Audio format
const QAudioFormat &fileFormat() const;
//! Audio format
const QAudioFormat &fileFormat() const;
//! Header length
qint64 headerLength() const;
//! Header length
qint64 headerLength() const;
//! The audio data
const QByteArray &audioData() const { return m_audioData; }
//! The audio data
const QByteArray &audioData() const { return m_audioData; }
private:
bool readHeader();
private:
bool readHeader();
QAudioFormat m_fileFormat;
qint64 m_headerLength;
QByteArray m_audioData;
};
} // ns
QAudioFormat m_fileFormat;
qint64 m_headerLength;
QByteArray m_audioData;
};
} // ns
#endif // guard