Передискретизация данных файла PCM с помощью soxr и libsndfile дает сбой

Я создаю приложение, которое частично должно передискретизировать любой входной аудиофайл PCM, который не имеет частоты 44 100 Гц, до 44,1 (или, по крайней мере, приложить все усилия для этого).

Для выполнения повторной выборки я использую soxr. soxr не имеет зависимостей и легковесен, что идеально подходит в данном случае, но он не предлагает собственного файлового ввода-вывода. У меня очень ограниченный опыт работы с потоками ввода-вывода в C, поэтому я упираюсь в стену. Приложение разрабатывается по модульному принципу, поэтому мне нужен процесс повторной выборки для создания выходного файла, который затем можно передать другим процессорам, а не просто работать с выходным потоком напрямую.

Чтобы создать этот выходной файл, я пытаюсь взять данные, сгенерированные процессом soxr повторной выборки, и передать их в libsndfile, который должен иметь возможность записывать звук в файл.

Ниже приведено чрезвычайно подробное объяснение того, где я нахожусь, хотя я не понимаю, почему он падает. Я подозреваю, что это как-то связано с тем, как распределяются и используются буферы. (Примечание: входной файл читается с помощью sndfile перед этим кодом)

(Вот суть всего)

Основные параметры ресемплера

// Use "high quality" resampling
unsigned int q_recipe = SOXR_HQ;

// No 
unsigned long q_flags = 0;

// Create the q_spec
soxr_quality_spec_t q_spec = soxr_quality_spec(q_recipe, q_flags);

Сопоставьте формат sndfile с форматом soxr

soxr_datatype_t itype;

// Get the SFINFO format
int iformat = self.inputFileInfo.format;

// Set the soxr itype to the corresponding format
if ((iformat & SF_FORMAT_FLOAT) == SF_FORMAT_FLOAT) {
  itype = SOXR_FLOAT32_S;
} else if ((iformat & SF_FORMAT_DOUBLE) == SF_FORMAT_DOUBLE) {
  itype = SOXR_FLOAT64_S;
} else if ((iformat & SF_FORMAT_PCM_32) == SF_FORMAT_PCM_32) {
  itype = SOXR_INT32_S;
} else {
  itype = SOXR_INT16_S;
}

Настройка спецификации ввода-вывода soxr

// Always want the output to match the input
soxr_datatype_t otype = itype;

soxr_io_spec_t io_spec = soxr_io_spec(itype, otype);

Резьба

// A single thread is fine
soxr_runtime_spec_t runtime_spec = soxr_runtime_spec(1);

Построить ресемплер

soxr_error_t error;

// Input rate can be read from the SFINFO    
double const irate = self.inputFileInfo.samplerate;

// Output rate is defined elsewhere, but this generally = 44100
double const orate = self.task.resampler.immutableConfiguration.targetSampleRate;

// Channel count also comes from SFINFO
unsigned chans = self.inputFileInfo.channels;

// Put it all together
soxr_t soxr = soxr_create(irate, orate, chans, &error, &io_spec, &q_spec, &runtime_spec);

Читать, пересэмплировать и писать

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

// Frames in sndfile are called Samples in soxr

// One frame is 1 item per channel
// ie frame_items = 1 item * channels
size_t const iframeitems = (1 * chans);

// item size is the data type size of the input type
//
size_t iitemsize;

if ((iformat & SF_FORMAT_FLOAT) == SF_FORMAT_FLOAT) {
  iitemsize = sizeof(Float32);
} else if ((iformat & SF_FORMAT_DOUBLE) == SF_FORMAT_DOUBLE) {
  iitemsize = sizeof(Float64);
} else if ((iformat & SF_FORMAT_PCM_32) == SF_FORMAT_PCM_32) {
  iitemsize = sizeof(int32_t);
} else {
  iitemsize = sizeof(int16_t);
}

// frame size is item size * items per frame (channels)
// eg for 2 channel 16 bit, frame size = 2 * 2
size_t const iframesize = (iframeitems * iitemsize);

// Number of frames to read (arbitrary)
sf_count_t const ireqframes = 1024;

// Size of the buffer is number of frames * size per frame
size_t const ibufsize = iframesize * ireqframes;

void *ibuf = malloc(ibufsize);

// Output
//////////////////////////////

// These match the input
size_t const oframeitems = iframeitems;
size_t const oitemsize = iitemsize;

// frame size is item size * items per frame (channels)
size_t const oframesize = (oframeitems * oitemsize);

// Number of frames expected after resampling
// eg
// orate = 44100
// irate = 48000
// ireqframe = 1024
// expect fewer frames (downsample)
// (44100 / 4800) * 1024 = 940.8
// Add 0.5 to deal with rounding?
sf_count_t const oexpframes = (ireqframes * (orate / irate)) + 0.5;

// Size of the buffer is number of frames * size per frame
size_t const obufsize = oframesize * oexpframes;

void *obuf = malloc(obufsize);

// Go
//////////////////////////////

size_t total_resample_output_frame_count = 0;
size_t need_input = 1;
sf_count_t num_frames_written = 0;

do {

  sf_count_t num_frames_read = 0;
  size_t actual_resample_output_samples = 0;

  // Read the input file based on its type
  // num_frames_read should be 1024
  if (otype == SOXR_INT16_S || otype == SOXR_INT32_S) {
    num_frames_read = sf_readf_int(self.inputFile, ibuf, ireqframes);
  } else if (otype == SOXR_FLOAT32_S) {
    num_frames_read = sf_readf_float(self.inputFile, ibuf, ireqframes);
  } else {
    num_frames_read = sf_readf_double(self.inputFile, ibuf, ireqframes);
  }

  // If there were no frames left to read we're done
  if (num_frames_read == 0) {
    // passing NULL input buffer to soxr_process indicates End-of-input
    ibuf = NULL;
    need_input = 0;
  }

  // Run the resampling on frames read from the input file
  error = soxr_process(soxr, ibuf, num_frames_read, NULL, obuf, oexpframes, &actual_resample_output_samples);

  total_resample_output_frame_count += actual_resample_output_samples;

  // Write the resulting data to output file
  // num_frames_written should = actual_resample_output_samples
  if (otype == SOXR_INT16_S || otype == SOXR_INT32_S) {
    num_frames_written = sf_writef_int(self.outputFile, obuf, actual_resample_output_samples);
  } else if (otype == SOXR_FLOAT32_S) {
    num_frames_written = sf_writef_float(self.outputFile, obuf, actual_resample_output_samples);
  } else {
    num_frames_written = sf_writef_double(self.outputFile, obuf, actual_resample_output_samples);
  }

} while (!error && need_input);

soxr_delete(soxr);
free(obuf), free(ibuf);

Это дает и EXC_BAD_ACCESS на soxr_process. Я понятия не имею, что еще попробовать на данный момент.


person farski    schedule 06.11.2014    source источник
comment
Ошибка при первом вызове sox_process или позже? Если на то пошло, вы когда-нибудь считывали кадры с sf_readf_*? Примеры, которые я вижу, похоже, используют размеры буфера, а не количество кадров для ввода размера в soxr_process, но это не должно вызывать катастрофических сбоев, если вы делаете это неправильно, просто пропускаете звук.   -  person hcs    schedule 08.11.2014
comment
Ошибка возникает при первом вызове soxc_process. В файлах, с которыми я это тестировал, num_frames_read = 1024 после sf_readf_; Я не совсем уверен, как проверить, являются ли биты в ibuf фактическими кадрами, но я подозреваю, что счетчик возврата является ожидаемым значением, которое он читает правильно. Я рассмотрю примеры для soxr, но в документации написано Input buf. длина (сэмплов на канал)., что заставляет меня думать, что он ожидает кадров.   -  person farski    schedule 10.11.2014
comment
Вы пытались отлаживать с помощью инструментов: stackoverflow.com/questions/19740200/ ?   -  person pmod    schedule 10.11.2014
comment
@pmod NSZombies здесь мало поможет, так как это не obj-c. Есть ли что-то еще в этой теме, что мне не хватает, что может мне помочь?   -  person farski    schedule 10.11.2014
comment
Ваш пример по сути, разве это не obj-c?   -  person pmod    schedule 10.11.2014
comment
Вы не показали свою часть sf_open_... - это может быть актуально здесь   -  person pmod    schedule 10.11.2014
comment
Этот код находится в проекте obj-c, но ничто из того, что здесь вызывает проблему, не является объектом, а NSZombies, на самом деле, влияет только на память объекта.   -  person farski    schedule 10.11.2014


Ответы (1)


_S в таких типах данных, как SOXR_INT32_S, означает, что вы используете разделенные каналы, и из примера 4-split-channels.c кажется, что в этом случае вам нужно передать массив указателей, по одному для каждого канала.

Однако в приведенном выше коде вы просто передаете один выделенный блок памяти, поэтому я предполагаю, что вы ожидаете данные канала с чередованием. Возможно, вы можете попробовать изменить _S на _I.

person brm    schedule 14.11.2014
comment
Как я узнаю, разделены ли данные файла или чередуются? Это то, что нормализует libsndfile, или я должен обнаруживать это в любой момент для других частей процесса? - person farski; 16.11.2014
comment
Хорошо, я внес это изменение, и он больше не падает! Звук очень испорчен, но, по крайней мере, я могу с этим работать. Спасибо! - person farski; 16.11.2014