Нормализация аудиосигнала - PullRequest
0 голосов
/ 11 июля 2020

У меня концептуальный вопрос о том, как цифровой звук хранится как в виде значений 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, который используется для чтения данных низкого уровня.

...