Проблема с записью 16-битного исходного файла PCM - PullRequest
5 голосов
/ 24 июля 2010

Как небольшая экспериментальная музыкальная пьеса, я пытаюсь запрограммировать песню в стандартном C. Код выводит необработанный файл PCM, который можно импортировать в Audacity. На данный момент все работает как положено, но я сталкиваюсь с проблемами при попытке записать каждый семпл как 16-битный в отличие от текущего 8-битного, который я использую.

Вплоть до момента записи текущая выборка рассчитывается как число с плавающей точкой, и ее границы в значительной степени находятся в диапазоне 8-разрядного целого числа со знаком. Затем оно записывается как 8-битное целое число, а затем повторяется процесс для следующего семпла. Это отлично работает и играет правильно. Проблема возникает, когда я пытаюсь записать его как 16-битный необработанный файл PCM - я умножаю число с плавающей запятой на 256 и копирую результат в целое число, после чего я использую fwrite для записи результирующего 16-битного целого числа. Это не дает ожидаемых результатов при импорте, что приводит к сильно искаженной версии того, что я ожидал.

Я добавил действительный код ниже, поскольку проблема возникает только на этапе записи.

Рабочий 8-битный код:

if (out<-127) {out=-128;} else if (out>126) {out=127;}
putc(out,fo);

Не работает 16-битный код:

if (out<-127) {out=-128;} else if (out>126) {out=127;}
pcm=out*256;
fwrite(&pcm,2,1,fo);

Возможно, я просто упускаю что-то очевидное, но я пытался решить это часами. Заранее спасибо!

Ответы [ 5 ]

4 голосов
/ 17 июля 2012

Я не могу сказать, что именно не так в вашем коде, не видя его, но это сделает вас прекрасной синусоидальной 16-битной PCM 1 кГц, открываемой в Audacity:

#include <stdio.h>
#include <math.h>

#ifndef M_PI
#define M_PI 3.14159265358
#endif

int main(void)
{
  FILE* f = fopen("sinewave.pcm", "wb");
  double t;
  for (t = 0; t < 1; t += 1./8000) // 8000 is the sample rate in Hz
  {
    double sample = 15000 * sin(2 * M_PI * 1000 * t); // 1000 Hz sine wave
    short s16 = (short)sample;
    unsigned char c;
    c = (unsigned)s16 % 256;
    fwrite(&c, 1, 1, f);
    c = (unsigned)s16 / 256 % 256;
    fwrite(&c, 1, 1, f);
  }
  fclose(f);
  return 0;
}

В Audacity перейдитечерез Файл-> Импорт-> Необработанные данные:

Кодировка: 16-битный PCM со знаком
Порядок следования байтов: Little-endian
Каналы: 1 канал (моно)
Частота дискретизации: 8000

Импорт.

3 голосов
/ 24 июля 2010

Я полагаю, что просмотр формы волны в Audacity дал бы вам некоторые подсказки.

Вы проверили:

  • Порядковый номер верен?
  • что вы не должны использовать, например, целые числа без знака?
  • вы правильно пометили файл как 16-битный?

Я не знаю, какой ожидаемый формат для PCM, но все они вероятные кандидаты в проблему.

0 голосов
/ 17 июля 2012

Для зомби этой темы:

Из WAV Wiki:

В формате WAV есть некоторые несоответствия: например, 8-битные данные не подписаны, а 16-битные подписаны

0 голосов
/ 24 июля 2010

Продолжаем здесь, но так как вам нужны 16-битные значения со знаком, попробуйте это:

int16_t pcm = out * 256;
fwrite(&pcm, sizeof(pcm), 1, fo);

Также убедитесь, что вы правильно отметили свой файл, т.е. необработанный PCM подписан 16 бит с соответствующим порядковым номером. (редактировать: это не относится к PCM)

0 голосов
/ 24 июля 2010

Хорошей практикой является приведение типов при преобразованиях.Например, если out - это число с плавающей запятой, то

putc((int) out, fo);

сообщит компилятору, что вы хотите записать свой номер в виде целого числа.

Конечно,Компилятор все равно поймет это для чего-то вроде putc , но для ссылок это не работает.Если вы объявите переменную pcm как число с плавающей запятой, то fwrite запишет данные с плавающей запятой вместо того, что вы хотите.Поэтому я задам тот же вопрос: является ли pcm целочисленным типом?

Еще один вопрос: вам действительно нужна плавающая точка здесь?Он может вам понадобиться, если вы можете использовать десятичную точность (опять же, вы потеряете эту точность при выводе в 8-битный или 16-битный формат), но это бесполезно, если вы будете делать простую математику со своими выборками.Поэтому вы можете значительно упростить задачу, придерживаясь целочисленного типа и преобразовывая его в char / int8_t при записи.

...