mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-05-01 14:45:42 +08:00
refs #144 , improved SELCAL in a way that SELCAL tones will always end with 0 amplitude.
* method to write values in buffer -> writeAmplitudeToBuffer * fixes the range from the last 0 pass so SELCAL tones will always end with 0 amplitude
This commit is contained in:
@@ -157,14 +157,14 @@ namespace BlackSound
|
|||||||
foreach(Tone t, this->m_tones)
|
foreach(Tone t, this->m_tones)
|
||||||
{
|
{
|
||||||
qint64 bytesPerTone = this->m_audioFormat.sampleRate() * bytesForAllChannels * t.m_durationMs / 1000;
|
qint64 bytesPerTone = this->m_audioFormat.sampleRate() * bytesForAllChannels * t.m_durationMs / 1000;
|
||||||
|
qint64 last0AmplitudeSample = bytesPerTone; // last sample when amplitude was 0
|
||||||
int sampleIndexPerTone = 0;
|
int sampleIndexPerTone = 0;
|
||||||
|
|
||||||
while (bytesPerTone)
|
while (bytesPerTone)
|
||||||
{
|
{
|
||||||
// http://hyperphysics.phy-astr.gsu.edu/hbase/audio/sumdif.html
|
// http://hyperphysics.phy-astr.gsu.edu/hbase/audio/sumdif.html
|
||||||
// http://math.stackexchange.com/questions/164369/how-do-you-calculate-the-frequency-perceived-by-humans-of-two-sinusoidal-waves-a
|
// http://math.stackexchange.com/questions/164369/how-do-you-calculate-the-frequency-perceived-by-humans-of-two-sinusoidal-waves-a
|
||||||
const double pseudoTime = double(sampleIndexPerTone % this->m_audioFormat.sampleRate()) / this->m_audioFormat.sampleRate();
|
const double pseudoTime = double(sampleIndexPerTone % this->m_audioFormat.sampleRate()) / this->m_audioFormat.sampleRate();
|
||||||
double amplitude = 0; // silence
|
double amplitude = 0.0; // amplitude -1 -> +1 , 0 is silence
|
||||||
if (t.m_frequencyHz > 10)
|
if (t.m_frequencyHz > 10)
|
||||||
{
|
{
|
||||||
// the combination of two frequencies actually would have 2*amplitude,
|
// the combination of two frequencies actually would have 2*amplitude,
|
||||||
@@ -176,40 +176,75 @@ namespace BlackSound
|
|||||||
qCos(M_PI * (t.m_frequencyHz - t.m_secondaryFrequencyHz) * pseudoTime);
|
qCos(M_PI * (t.m_frequencyHz - t.m_secondaryFrequencyHz) * pseudoTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// avoid overflow
|
||||||
|
Q_ASSERT(amplitude <= 1.0 && amplitude >= -1.0);
|
||||||
|
if (amplitude < -1.0)
|
||||||
|
amplitude = -1.0;
|
||||||
|
else if (amplitude > 1.0)
|
||||||
|
amplitude = 1.0;
|
||||||
|
else if (qAbs(amplitude) < double(1.0 / 65535))
|
||||||
|
{
|
||||||
|
amplitude = 0;
|
||||||
|
last0AmplitudeSample = bytesPerTone;
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate this for all channels, usually 1 channel
|
||||||
for (int i = 0; i < this->m_audioFormat.channelCount(); ++i)
|
for (int i = 0; i < this->m_audioFormat.channelCount(); ++i)
|
||||||
{
|
{
|
||||||
if (this->m_audioFormat.sampleSize() == 8 && this->m_audioFormat.sampleType() == QAudioFormat::UnSignedInt)
|
this->writeAmplitudeToBuffer(amplitude, bufferPointer);
|
||||||
{
|
|
||||||
const quint8 value = static_cast<quint8>((1.0 + amplitude) / 2 * 255);
|
|
||||||
*reinterpret_cast<quint8 *>(bufferPointer) = value;
|
|
||||||
}
|
|
||||||
else if (this->m_audioFormat.sampleSize() == 8 && this->m_audioFormat.sampleType() == QAudioFormat::SignedInt)
|
|
||||||
{
|
|
||||||
const qint8 value = static_cast<qint8>(amplitude * 127);
|
|
||||||
*reinterpret_cast<quint8 *>(bufferPointer) = value;
|
|
||||||
}
|
|
||||||
else if (this->m_audioFormat.sampleSize() == 16 && this->m_audioFormat.sampleType() == QAudioFormat::UnSignedInt)
|
|
||||||
{
|
|
||||||
quint16 value = static_cast<quint16>((1.0 + amplitude) / 2 * 65535);
|
|
||||||
if (this->m_audioFormat.byteOrder() == QAudioFormat::LittleEndian)
|
|
||||||
qToLittleEndian<quint16>(value, bufferPointer);
|
|
||||||
else
|
|
||||||
qToBigEndian<quint16>(value, bufferPointer);
|
|
||||||
}
|
|
||||||
else if (this->m_audioFormat.sampleSize() == 16 && this->m_audioFormat.sampleType() == QAudioFormat::SignedInt)
|
|
||||||
{
|
|
||||||
qint16 value = static_cast<qint16>(amplitude * 32767);
|
|
||||||
if (this->m_audioFormat.byteOrder() == QAudioFormat::LittleEndian)
|
|
||||||
qToLittleEndian<qint16>(value, bufferPointer);
|
|
||||||
else
|
|
||||||
qToBigEndian<qint16>(value, bufferPointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
bufferPointer += bytesPerSample;
|
bufferPointer += bytesPerSample;
|
||||||
bytesPerTone -= bytesPerSample;
|
bytesPerTone -= bytesPerSample;
|
||||||
}
|
}
|
||||||
++sampleIndexPerTone;
|
++sampleIndexPerTone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fixes the range from the last 0 pass through
|
||||||
|
if (last0AmplitudeSample > 0)
|
||||||
|
{
|
||||||
|
bufferPointer -= last0AmplitudeSample;
|
||||||
|
while (last0AmplitudeSample)
|
||||||
|
{
|
||||||
|
double amplitude = 0.0; // amplitude -1 -> +1 , 0 is silence
|
||||||
|
|
||||||
|
// generate this for all channels, usually 1 channel
|
||||||
|
for (int i = 0; i < this->m_audioFormat.channelCount(); ++i)
|
||||||
|
{
|
||||||
|
this->writeAmplitudeToBuffer(amplitude, bufferPointer);
|
||||||
|
bufferPointer += bytesPerSample;
|
||||||
|
last0AmplitudeSample -= bytesPerSample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSoundGenerator::writeAmplitudeToBuffer(const double amplitude, unsigned char *bufferPointer)
|
||||||
|
{
|
||||||
|
if (this->m_audioFormat.sampleSize() == 8 && this->m_audioFormat.sampleType() == QAudioFormat::UnSignedInt)
|
||||||
|
{
|
||||||
|
const quint8 value = static_cast<quint8>((1.0 + amplitude) / 2 * 255);
|
||||||
|
*reinterpret_cast<quint8 *>(bufferPointer) = value;
|
||||||
|
}
|
||||||
|
else if (this->m_audioFormat.sampleSize() == 8 && this->m_audioFormat.sampleType() == QAudioFormat::SignedInt)
|
||||||
|
{
|
||||||
|
const qint8 value = static_cast<qint8>(amplitude * 127);
|
||||||
|
*reinterpret_cast<quint8 *>(bufferPointer) = value;
|
||||||
|
}
|
||||||
|
else if (this->m_audioFormat.sampleSize() == 16 && this->m_audioFormat.sampleType() == QAudioFormat::UnSignedInt)
|
||||||
|
{
|
||||||
|
quint16 value = static_cast<quint16>((1.0 + amplitude) / 2 * 65535);
|
||||||
|
if (this->m_audioFormat.byteOrder() == QAudioFormat::LittleEndian)
|
||||||
|
qToLittleEndian<quint16>(value, bufferPointer);
|
||||||
|
else
|
||||||
|
qToBigEndian<quint16>(value, bufferPointer);
|
||||||
|
}
|
||||||
|
else if (this->m_audioFormat.sampleSize() == 16 && this->m_audioFormat.sampleType() == QAudioFormat::SignedInt)
|
||||||
|
{
|
||||||
|
qint16 value = static_cast<qint16>(amplitude * 32767);
|
||||||
|
if (this->m_audioFormat.byteOrder() == QAudioFormat::LittleEndian)
|
||||||
|
qToLittleEndian<qint16>(value, bufferPointer);
|
||||||
|
else
|
||||||
|
qToBigEndian<qint16>(value, bufferPointer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,7 +485,6 @@ namespace BlackSound
|
|||||||
void CSoundGenerator::playFile(qint32 volume, const QString &file, bool removeFileAfterPlaying)
|
void CSoundGenerator::playFile(qint32 volume, const QString &file, bool removeFileAfterPlaying)
|
||||||
{
|
{
|
||||||
if (!QFile::exists(file)) return;
|
if (!QFile::exists(file)) return;
|
||||||
|
|
||||||
QMediaPlayer *mediaPlayer = CSoundGenerator::mediaPlayer();
|
QMediaPlayer *mediaPlayer = CSoundGenerator::mediaPlayer();
|
||||||
QMediaResource mediaResource(QUrl(file), "audio");
|
QMediaResource mediaResource(QUrl(file), "audio");
|
||||||
QMediaContent media(mediaResource);
|
QMediaContent media(mediaResource);
|
||||||
|
|||||||
@@ -344,6 +344,12 @@ namespace BlackSound
|
|||||||
//! \brief save buffer to wav file
|
//! \brief save buffer to wav file
|
||||||
bool saveToWavFile(const QString &fileName) const;
|
bool saveToWavFile(const QString &fileName) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Write amplitude to buffer
|
||||||
|
* \param amplitude value -1 .. 1
|
||||||
|
* \param bufferPointer current buffer pointer
|
||||||
|
*/
|
||||||
|
void writeAmplitudeToBuffer(const double amplitude, unsigned char *bufferPointer);
|
||||||
};
|
};
|
||||||
} //namespace
|
} //namespace
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user