У меня концептуальный вопрос о том, как цифровой звук хранится как в виде значений int со знаком и без знака.
Насколько я понимаю (давайте возьмем 16-битный звук, хранящийся как целые числа со знаком):
- Диапазон сигнала от -32768 до 32767
- 0 представляет среднюю точку сигнала; поэтому, когда нет звука, ваш сигнал должен находиться на уровне 0
Итак, если вы хотите нормализовать этот сигнал, чтобы указать диапазон 0-1, ваша средняя точка сигнала будет 0,5
Когда я обрабатываю 16-битные файлы WAV, это именно то, что я вижу, что упрощает нормализацию.
Проблема в том, что когда я воспроизводю 8-битный подписанный звук, скажем, из 'Stereo Mix' в Windows, я получаю значение -128 вместо 0, когда нет звука. В противном случае сигнал имеет диапазон от -128 до 127.
- Верно ли мое предположение о том, что средняя точка равна 0 для целых чисел со знаком?
- Если нет, то какова правильная средняя точка и почему разница между 8-битным и 16-битным целочисленным звуком со знаком?
- Какими будут диапазон и средняя точка, например, для 8-битных целых чисел без знака? От 0 до 255 с 128 в качестве средней точки?
Это код QT, адаптированный из примера, который я использую для определения аудиоформата и чтения входящих данных:
AudioIODevice::AudioIODevice(QObject *parent, const QAudioFormat &deviceFormat) :
QIODevice(parent),
format(deviceFormat)
{
int sampleSize = format.sampleSize();
switch (format.sampleType())
{
case QAudioFormat::UnSignedInt:
minValue = 0.0f;
maxValue = static_cast<float>(std::pow(2,sampleSize) - 1);
break;
case QAudioFormat::SignedInt:
minValue = static_cast<float>((std::pow(2,sampleSize)/2) * (-1));
maxValue = static_cast<float>((std::pow(2,sampleSize)/2) - 1);
break;
case QAudioFormat::Float:
break;
default:
break;
}
}
qint64 AudioIODevice::writeData(const char *data, qint64 len)
{
unsigned int sampleBytes = format.sampleSize() / 8; //Number of bytes for each interleaved channel sample
unsigned int combSampleBytes = format.channelCount() * sampleBytes; //Number of bytes for all channel samples
unsigned int numSamples = len / combSampleBytes; //Total number of samples
if(format.sampleSize() % 8 != 0 || len % sampleBytes != 0)
return -1;
//Prepare our output buffer
buffer.clear();
buffer.resize(numSamples,0);
const unsigned char* uData = reinterpret_cast<const unsigned char*>(data);
for(unsigned int i = 0; i < numSamples; i++)
{
float monoValue = minValue;
float value = minValue;
//Process data for all interleaved samples
for(unsigned int j = 0; j < format.channelCount(); j++)
{
switch (format.sampleType())
{
case QAudioFormat::UnSignedInt:
switch(format.sampleSize())
{
case 8:
value = *reinterpret_cast<const quint8*>(uData);
break;
case 16:
value = (format.byteOrder()==QAudioFormat::LittleEndian)?
(qFromLittleEndian<quint16>(*reinterpret_cast<const quint16*>(uData))):
(qFromBigEndian<quint16>(*reinterpret_cast<const quint16*>(uData)));
break;
case 32:
value = (format.byteOrder()==QAudioFormat::LittleEndian)?
(qFromLittleEndian<quint32>(*reinterpret_cast<const quint32*>(uData))):
(qFromBigEndian<quint32>(*reinterpret_cast<const quint32*>(uData)));
break;
default:
break;
}
break;
case QAudioFormat::SignedInt:
switch(format.sampleSize())
{
case 8:
value = *reinterpret_cast<const qint8*>(uData);
break;
case 16:
value = (format.byteOrder()==QAudioFormat::LittleEndian)?
(qFromLittleEndian<qint16>(*reinterpret_cast<const qint16*>(uData))):
(qFromBigEndian<qint16>(*reinterpret_cast<const qint16*>(uData)));
break;
case 32:
value = (format.byteOrder()==QAudioFormat::LittleEndian)?
(qFromLittleEndian<qint32>(*reinterpret_cast<const qint32*>(uData))):
(qFromBigEndian<qint32>(*reinterpret_cast<const qint32*>(uData)));
break;
default:
break;
}
break;
case QAudioFormat::Float:
break;
default:
break;
}
monoValue = std::max(value,monoValue);
uData += sampleBytes; //Get data for the next sample
}
buffer[i] = (monoValue - minValue) / (maxValue - minValue); //Normalize the value to [0-1]
}
emit bufferReady();
return len;
}
Класс AudioIODevice наследует QIODevice QT, который используется для чтения данных низкого уровня.