Как преобразовать образцы pcm в массив байтов как числа с плавающей запятой в диапазоне от -1,0 до 1,0 и обратно? - программирование
Подтвердить что ты не робот

Как преобразовать образцы pcm в массив байтов как числа с плавающей запятой в диапазоне от -1,0 до 1,0 и обратно?

В алгоритме повторной выборки я используется массив float, содержащий входные образцы в диапазоне от -1.0 до 1.0. Аудиоданные 16 бит PCM с образцом 22 кГц.

Я хочу уменьшить размер звука с 22 кГц до 8 кГц, как представить образцы в массиве байтов как числа с плавающей запятой >= -1 и <= 1 и вернуться к массиву байтов?

4b9b3361

Ответ 1

Вы задаете два вопроса:

  • Как сжать от 22 кГц до 8 кГц?

  • Как преобразовать из float [-1,1] в 16-битный int и обратно?

Обратите внимание, что вопрос обновлен, чтобы указать, что № 1 позаботится о другом, но я оставлю эту часть своего ответа, если это поможет кому-то другому.

1. Как сжать от 22 кГц до 8 кГц?

Комментирующий намекнул, что это можно решить с помощью FFT. Это неверно (один шаг в передискретизации - фильтрация. Я упоминаю, почему бы не использовать FFT для фильтрации здесь, если вы заинтересованы: http://blog.bjornroche.com/2012/08/when-to-not-use-fft.html).

Один очень хороший способ перепрограммировать сигнал - это многофазный фильтр. Однако это довольно сложно, даже для тех, кто имеет опыт обработки сигналов. У вас есть несколько других вариантов:

  • используйте библиотеку, которая реализует повторную выборку высокого качества, например libsamplerate
  • сделать что-то быстрое и грязное

Похоже, что вы уже пошли с первым подходом, что здорово.

Быстрое и грязное решение не будет звучать так же хорошо, но, поскольку вы снижаетесь до 8 кГц, я предполагаю, что качество звука - не ваш первый приоритет. Один быстрый и грязный вариант:

  • Применить фильтр нижних частот к сигналу. Постарайтесь избавиться от звука, превышающего 4 кГц, как вы можете. Вы можете использовать фильтры, описанные здесь (хотя в идеале вы хотите что-то гораздо круче, чем эти фильтры, они, по крайней мере, лучше, чем ничего).
  • выберите каждый 2.75-й образец из исходного сигнала, чтобы создать новый, повторно выбранный сигнал. Когда вам нужен нецелый образец, используйте линейную интерполяцию. Если вам нужна помощь в линейной интерполяции, попробуйте здесь.

Этот метод должен быть более чем достаточно для голосовых приложений. Тем не менее, я не пробовал, поэтому я не знаю точно, поэтому настоятельно рекомендую использовать другую библиотеку.

Если вы действительно хотите реализовать собственное преобразование частоты дискретизации высокого качества, такое как многофазный фильтр, вам следует изучить его, а затем задать любые вопросы, которые у вас есть, https://dsp.stackexchange.com/, а не здесь.

2. Как конвертировать из float [-1,1] в 16-бит int и обратно?

Это началось c.fogelklou уже, но позвольте мне украсить.

Для начала, диапазон 16-битных целых чисел составляет от -32768 до 32767 (обычно 16-битный звук подписывается). Для преобразования из int в float вы делаете это:

float f;
int16 i = ...;
f = ((float) i) / (float) 32768
if( f > 1 ) f = 1;
if( f < -1 ) f = -1;

Обычно вам не нужно делать лишние "ограничивающие" (на самом деле это не так, если вы действительно используете 16-битное целое число), но это там, если у вас есть некоторые > 16-битные целые числа по какой-либо причине.

Чтобы преобразовать назад, вы делаете это:

float f = ...;
int16 i;
f = f * 32768 ;
if( f > 32767 ) f = 32767;
if( f < -32768 ) f = -32768;
i = (int16) f;

В этом случае обычно необходимо следить за значениями вне диапазона, особенно значениями больше 32767. Вы можете пожаловаться, что это приводит к некоторым искажениям для f = 1. Этот вопрос горячо обсуждается. Для некоторого (неполного) обсуждения этого, см. это сообщение в блоге.

Это больше, чем "достаточно хорошо для работы правительства". Другими словами, он будет работать нормально, за исключением случаев, когда вы обеспокоены качеством звука. Поскольку вы собираетесь на 8 кГц, я думаю, мы установили, что это не так, поэтому этот ответ в порядке.

Однако, для полноты, я должен добавить это: если вы пытаетесь сохранить вещи абсолютно нетронутыми, имейте в виду, что это преобразование вводит искажения. Зачем? Поскольку ошибка при преобразовании из float в int коррелирует с сигналом. Оказывается, корреляция этой ошибки ужасна, и вы действительно можете ее услышать, хотя она очень маленькая. (к счастью, он достаточно мал, что для таких вещей, как речь и низкодинамичная музыка диапазона, это не имеет большого значения). Чтобы устранить эту ошибку, вы должны использовать что-то, называемое dither в преобразовании из float в int. Опять же, если вам что-то нужно, исследуйте его и задайте соответствующие конкретные вопросы https://dsp.stackexchange.com/, а не здесь.

Вы также можете быть заинтересованы в слайдах из моего рассказа об основах цифрового аудиопрограммирования, в котором есть слайд на эту тему, хотя в основном он говорит то же самое (может быть, даже меньше, чем я только что сказал): http://blog.bjornroche.com/2011/11/slides-from-fundamentals-of-audio.html

Ответ 2

16-битный PCM имеет диапазон - от 32768 до 32767. Таким образом, умножьте каждый из ваших образцов PCM на (1.0f/32768.0f) в новый массив поплавков и передайте это вашему повторному набору.

Возвращаясь к float после повторной дискретизации, умножьте на 32768.0, насытите (закрепите что-нибудь за пределами диапазона - 32768 до 32767), округлите (или смените, как упомянул Бьорн), а затем отбросьте на короткий.

Тестовый код, который показывает преобразование вперед и назад с использованием умножений без ошибок в битах:

// PcmConvertTest.cpp : Defines the entry point for the console application.
//

#include <assert.h>
#include <string.h>
#include <stdint.h>
#define SZ 65536
#define MAX(x,y) ((x)>(y)) ? (x) : (y)
#define MIN(x,y) ((x)<(y)) ? (x) : (y)
int main(int argc, char* argv[])
{
  int16_t *pIntBuf1 = new int16_t[SZ];
  int16_t *pIntBuf2 = new int16_t[SZ];
  float   *pFloatBuf = new float[SZ];

  // Create an initial short buffer for testing
  for( int i = 0; i < SZ; i++) {
    pIntBuf1[i] = (int16_t)(-32768 + i);
  }

  // Convert the buffer to floats. (before resampling)
  const float div = (1.0f/32768.0f);
  for( int i = 0; i < SZ; i++) {
    pFloatBuf[i] = div * (float)pIntBuf1[i];
  }

  // Convert back to shorts
  const float mul = (32768.0f);
  for( int i = 0; i < SZ; i++) {
    int32_t tmp = (int32_t)(mul * pFloatBuf[i]);
    tmp = MAX( tmp, -32768 ); // CLIP < 32768
    tmp = MIN( tmp, 32767 );  // CLIP > 32767
    pIntBuf2[i] = tmp;
  }

  // Check that the conversion went int16_t to float and back to int for every PCM value without any errors.
  assert( 0 == memcmp( pIntBuf1, pIntBuf2, sizeof(int16_t) * SZ) );

  delete pIntBuf1;
  delete pIntBuf2;
  delete pFloatBuf;
  return 0;
}