c – 使用AudioFileServices在iOS 6中进行粒度合成

我有一个关于我正在研究的声音合成应用程序的问题.我试图读取一个音频文件,使用
granular synthesis techniques创建随机’粒子’,将它们放入输出缓冲区,然后能够使用OpenAL将其播放回用户.出于测试目的,我只是将输出缓冲区写入一个文件然后我可以回听.

从我的结果来看,我在正确的轨道上,但我得到一些混叠问题和播放声音似乎不太正确.输出文件中间通常有一个相当响亮的弹出,音量水平有时很大.

以下是我为获得所需结果而采取的步骤,但我对一些事情感到困惑,即我为AudioStreamBasicDescription指定的格式.

>从我的mainBundle读取一个音频文件,这是一个.aiff格式的单声道文件:

ExtAudioFileRef extAudioFile;
CheckError(ExtAudioFileOpenURL(loopFileURL,
                           &extAudioFile),
       "couldn't open extaudiofile for reading");
memset(&player->dataFormat, 0, sizeof(player->dataFormat));

player->dataFormat.mFormatID = kAudioFormatLinearPCM;
player->dataFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
player->dataFormat.mSampleRate = S_RATE;
player->dataFormat.mChannelsPerFrame = 1;
player->dataFormat.mFramesPerPacket = 1;
player->dataFormat.mBitsPerChannel = 16;
player->dataFormat.mBytesPerFrame = 2;
player->dataFormat.mBytesPerPacket = 2;

// tell extaudiofile about our format
CheckError(ExtAudioFileSetProperty(extAudioFile,
                               kExtAudioFileProperty_ClientDataFormat,
                               sizeof(AudioStreamBasicDescription),
                               &player->dataFormat),
       "couldnt set client format on extaudiofile");

SInt64 fileLengthFrames;
UInt32 propSize = sizeof(fileLengthFrames);
ExtAudioFileGetProperty(extAudioFile,
                    kExtAudioFileProperty_FileLengthFrames,
                    &propSize,
                    &fileLengthFrames);

player->bufferSizeBytes = fileLengthFrames * player->dataFormat.mBytesPerFrame;

>接下来,我声明我的AudioBufferList并设置更多属性

AudioBufferList *buffers;
UInt32 ablSize = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * 1);
buffers = (AudioBufferList *)malloc(ablSize);

player->sampleBuffer = (SInt16 *)malloc(sizeof(SInt16) * player->bufferSizeBytes);

buffers->mNumberBuffers = 1;
buffers->mBuffers[0].mNumberChannels = 1;
buffers->mBuffers[0].mDataByteSize = player->bufferSizeBytes;
buffers->mBuffers[0].mData = player->sampleBuffer;

>我的理解是.mData将是formatFlags中指定的任何内容(在本例中,键入SInt16).由于它是类型(void *),我想将其转换为浮点数据,这对于音频操作来说是显而易见的.在我设置一个for循环之前,它只迭代缓冲区并将每个样本转换为float *.这似乎是不必要的,所以现在我将.mData缓冲区传递给我创建的函数,然后对音频进行粒化:

    float *theOutBuffer = [self granularizeWithData:(float *)buffers->mBuffers[0].mData with:framesRead];

>在这个函数中,我动态分配一些缓冲区,创建随机大小的粒子,在使用汉明窗口窗口化后将它们放在我的输出缓冲区中并返回该缓冲区(这是浮点数据).到目前为止,一切都很酷.
>接下来我设置了所有输出文件ASBD等:

AudioStreamBasicDescription outputFileFormat;

bzero(audioFormatPtr, sizeof(AudioStreamBasicDescription));

outputFileFormat->mFormatID = kAudioFormatLinearPCM;
outputFileFormat->mSampleRate = 44100.0;
outputFileFormat->mChannelsPerFrame = numChannels;
outputFileFormat->mBytesPerPacket = 2 * numChannels;
outputFileFormat->mFramesPerPacket = 1;
outputFileFormat->mBytesPerFrame = 2 * numChannels;
outputFileFormat->mBitsPerChannel = 16;
outputFileFormat->mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked;

UInt32 flags = kAudioFileFlags_EraseFile;
ExtAudioFileRef outputAudioFileRef = NULL;
NSString *tmpDir = NSTemporaryDirectory();
NSString *outFilename = @"Decomp.caf";
NSString *outPath = [tmpDir stringByAppendingPathComponent:outFilename];
NSURL *outURL = [NSURL fileURLWithPath:outPath];


AudioBufferList *outBuff;
UInt32 abSize = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * 1);
outBuff = (AudioBufferList *)malloc(abSize);

outBuff->mNumberBuffers = 1;
outBuff->mBuffers[0].mNumberChannels = 1;
outBuff->mBuffers[0].mDataByteSize = abSize;
outBuff->mBuffers[0].mData = theOutBuffer;

CheckError(ExtAudioFileCreateWithURL((__bridge CFURLRef)outURL,
                                 kAudioFileCAFType,
                                 &outputFileFormat,
                                 NULL,
                                 flags,
                                 &outputAudioFileRef),
       "ErrorCreatingURL_For_EXTAUDIOFILE");

CheckError(ExtAudioFileSetProperty(outputAudioFileRef,
                               kExtAudioFileProperty_ClientDataFormat,
                               sizeof(outputFileFormat),
                               &outputFileFormat),
       "ErrorSettingProperty_For_EXTAUDIOFILE");

CheckError(ExtAudioFileWrite(outputAudioFileRef,
                         framesRead,
                         outBuff),
       "ErrorWritingFile");

该文件以CAF格式正确写入.我的问题是:我是否正确处理.mData缓冲区,因为我正在将样本转换为浮动数据,操纵(粒化)各种窗口大小,然后使用ExtAudioFileWrite(CAF格式)将其写入文件?有没有更优雅的方法来执行此操作,例如将我的ASBD formatFlag声明为kAudioFlagIsFloat?我的输出CAF文件中有一些点击,当我在Logic中打开它时,看起来有很多别名.这是有道理的,如果我试图发送它浮动数据,但有一种转换发生,我不知道.

提前感谢您对此事的任何建议!我一直是网上几乎所有源材料的狂热读者,包括核心音频书,各种博客,教程等.我的应用程序的最终目标是通过耳机实时向用户播放粒化音频,以便写入文件的东西目前只是用于测试.谢谢!

最佳答案 你对第3步的看法告诉我你是将一系列短裤解释为一系列花车?如果是这样,我们找到了你的麻烦的原因.你可以将短值一个一个地分配到一个浮点数组中吗?那应该解决它.

看起来mData是一个指向一组short的void *.将此指针转换为float *不会将基础数据更改为float,但您的音频处理函数会将它们视为它们.但是,浮点值和短值以完全不同的方式存储,因此您在该函数中执行的数学运算将使用与您的真实输入信号无关的非常不同的值.要通过实验进行调查,请尝试以下操作:

short data[4] = {-27158, 16825, 23024, 15};
void *pData = data;

void指针不指示它指向哪种数据,因此错误地,可以错误地假设它指向浮点值.注意,short是2字节宽,但float是4字节宽.巧合的是,您的代码没有因访问冲突而崩溃.解释为float,上面的数组只有两个值足够长.我们来看看第一个值:

float *pfData = (float *)pData;
printf("%d == %f\n", data[0], pfData[0]);

这个输出将是-27158 == 23.198200,说明你如何获得大约23.2f而不是预期的-27158.0f.发生了两件有问题的事情.首先,sizeof(float)不是sizeof(short).其次,浮点数的“1和0”与整数的存储方式非常不同.见http://en.wikipedia.org/wiki/Single_precision_floating-point_format.

如何解决问题?至少有两个简单的解决方案.首先,您可以在将数组输入音频处理器之前转换它的每个元素:

int k;
float *pfBuf = (float *)malloc(n_data * sizeof(float));
short *psiBuf = (short *)buffers->mBuffers[0].mData[k];
for (k = 0; k < n_data; k ++)
{
    pfBuf[k] = psiBuf[k];
}
[self granularizeWithData:pfBuf with:framesRead];
for (k = 0; k < n_data; k ++)
{
    psiBuf[k] = pfBuf[k];
}
free(pfBuf);

你看到,在你调用granularizeWithData之后,你很可能必须将所有东西都转换成简短的:with:.所以第二种解决方案是简单地完成所有处理,尽管从你写的内容来看,我想你不会喜欢后一种方法.

点赞