在IOS 6使用AudioFileServices粒状合成(Granular Synthesis i

2019-08-04 14:26发布

我有一个关于我在做一个声音合成应用程序的问题。 我试图在音频文件读取,创建随机的“杂粮”使用颗粒合成技术 ,将它们放置到输出缓冲区,然后能够播放回使用OpenAL的用户。 出于测试目的,我只是写输出缓冲区中的文件,我可以再听听回。

看我的成绩,我在正确的轨道上,但我得到一些混淆的问题和播放声音,只是似乎不太对劲。 通常有输出文件和音量水平中间的相当响亮的流行是在时间非常响亮。

下面是我已得到我需要的结果的步骤,但我有点困惑的几件事情,即格式化,我指定我AudioStreamBasicDescription。

  1. 阅读从我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; 
  2. 接下来我宣布我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; 
  3. 我的理解是,.mData会无论是在formatFlags(在这种情况下,键入SInt16)指定。 因为它的类型(void *的),我想将它转换为浮动这是音频处理明显的数据。 之前,我成立了一个for循环刚刚通过缓冲迭代和每个样本转换为浮动*。 这似乎没有必要那么现在我通过我的.mData缓冲区我创建了一个函数,然后granularizes音频:

      float *theOutBuffer = [self granularizeWithData:(float *)buffers->mBuffers[0].mData with:framesRead]; 
  4. 在这个函数中,我动态地分配一些缓冲区,创建任意大小粒,将它们放置在我出缓冲器使用汉明窗开窗他们后返回缓冲区(这就是浮存数据)。 一切都很酷了这一点。

  5. 接下来,我建立了我的所有输出文件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文件中有一些点击,当我在逻辑打开它,它看起来像有很多混淆的。 这是有道理的,如果我试图把它漂浮数据,但有某种转换发生的这我不知道的。

在此先感谢对此事的任何建议! 我一直在几乎所有的源材料的热心读者在网上,包括Core声读物,各种博客,教程等我的应用程序的最终目的是用耳机,因此发挥实时颗粒化音频的用户写入文件的事只是被用于目前的测试。 谢谢!

Answer 1:

你说的有关步骤3的建议给我你解释短裤的数组作为float数组? 如果是这样的话,我们发现你麻烦的原因。 您可将短值一个接一个在float数组? 这应该修复它。

它看起来像mData是一个void *指向短裤的阵列。 铸造这个指针float *的基础数据不变成float ,但你的音频处理功能将对待他们,如果他们。 然而, floatshort值存储在完全不同的方式,让你在这个函数做数学题,将运行在具有无关,与你的真实的输入信号非常不同的值。 为了在实验上研究这个,请尝试以下操作:

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

void指针并不表示它指向的是什么样的数据,所以误,可以错误地认为它指向的float值。 注意, short为2字节宽,但float是4字节宽。 它是你的代码没有与访问冲突崩溃巧合。 解释为float上面的阵列仅用于两个值足够长的时间。 让我们看一看第一个值:

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

这样做的输出将是-27158 == 23.198200说明的是如何,而不是预期的-27158.0f你获得大致23.2f 。 两个问题的事情发生了。 首先, sizeof(float)不是sizeof(short) 。 二,“一和零”一个浮点数的存储不是一个整数非常不同。 见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);

你看,最有可能你将不得不一切转换回short您的来电后granularizeWithData: with: 。 因此,第二个解决办法是做所有的处理在short虽然从你写的是什么,我想你也不会喜欢后一种方式。



文章来源: Granular Synthesis in iOS 6 using AudioFileServices