【发布时间】:2015-01-02 11:26:27
【问题描述】:
我正在构建一个应用程序,它需要将任何不是 44100Hz 的输入 PCM 音频文件重新采样到 44.1(或至少尽最大努力这样做)。
为了处理重采样,我使用了soxr。 soxr 没有依赖关系并且是轻量级的,这在这种情况下是理想的,但它不提供本机文件 I/O。我对 C 中的 IO 流的经验非常有限,所以我碰壁了。该应用程序是模块化设计的,因此我需要重新采样过程来创建一个输出文件,然后可以将其传递给其他处理器,而不是简单地直接处理输出流。
为了创建该输出文件,我正在尝试获取soxr 重采样过程生成的数据,并将其传递给libsndfile,它应该能够将音频写入文件。
下面是对我所在位置的极其详细的解释,尽管我不知道它为什么会崩溃。我怀疑这与缓冲区的分配和使用方式有关。 (注意:输入文件是在此代码之前使用 sndfile 读取的)
(这是整个事情的single gist)
基本的重采样器选项
// 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 IO 规范
// 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);
这会在soxr_process 上给出 EXC_BAD_ACCESS。在这一点上,我不知道还有什么可以尝试的。
【问题讨论】:
-
是第一次调用
sox_process时出错,还是以后出现错误?就此而言,您是否曾经从sf_readf_*中读取过一些帧?我看到的示例似乎使用缓冲区大小而不是帧数作为soxr_process的大小输入,但如果你做错了,这不应该导致灾难性的失败,只是跳过了音频。 -
第一次调用
soxc_process时确实发生了错误。在我测试过的文件上,num_frames_read = 1024在sf_readf_之后;我不太确定如何检查ibuf中的位是否是实际帧,但我怀疑返回计数是否是正确读取的预期值。我将查看 soxr 的示例,但文档说“输入 buf。长度(每个通道的样本)。”,这让我认为它期待帧。 -
你试过用工具调试吗:stackoverflow.com/questions/19740200/…?
-
@pmod NSZombies 在这里没有多大帮助,因为这不是 obj-c。该线程中还有其他我想念的东西可以帮助我吗?
-
你在 gist 上的例子,不是 obj-c 吗?
标签: c signal-processing pcm sox libsndfile