Я создаю приложение, которое частично должно передискретизировать любой входной аудиофайл 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
. Я понятия не имею, что еще попробовать на данный момент.
sox_process
или позже? Если на то пошло, вы когда-нибудь считывали кадры сsf_readf_*
? Примеры, которые я вижу, похоже, используют размеры буфера, а не количество кадров для ввода размера вsoxr_process
, но это не должно вызывать катастрофических сбоев, если вы делаете это неправильно, просто пропускаете звук. - person hcs   schedule 08.11.2014soxc_process
. В файлах, с которыми я это тестировал,num_frames_read = 1024
послеsf_readf_
; Я не совсем уверен, как проверить, являются ли биты вibuf
фактическими кадрами, но я подозреваю, что счетчик возврата является ожидаемым значением, которое он читает правильно. Я рассмотрю примеры для soxr, но в документации написано Input buf. длина (сэмплов на канал)., что заставляет меня думать, что он ожидает кадров. - person farski   schedule 10.11.2014