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

2024-04-09

我对我正在开发的声音合成应用程序有疑问。我正在尝试读取音频文件,使用创建随机“颗粒”颗粒合成技术 http://en.wikipedia.org/wiki/Granular_synthesis,将它们放入输出缓冲区,然后能够使用 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 循环之前,它只是迭代缓冲区并将每个样本转换为 float*。这似乎没有必要,所以现在我将 .mData 缓冲区传递给我创建的函数,然后该函数对音频进行粒度化:

        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 文件中有一些点击声,当我在 Logic 中打开它时,看起来有很多锯齿。如果我尝试向它发送浮点数据,但发生了某种我不知道的转换,这是有道理的。

预先感谢您对此事的任何建议!我一直是几乎所有在线源材料的狂热读者,包括核心有声读物、各种博客、教程等。我的应用程序的最终目标是向戴着耳机的用户实时播放颗粒化音频,以便写入文件目前仅用于测试。谢谢!


你对步骤 3 的说法表明你正在将一组短裤解释为一组浮点数?如果是这样,我们就找到了您遇到麻烦的原因。您可以将短值一一分配到浮点数组中吗?那应该解决它。

看起来像mData is a void *指着一排短裤。将此指针转换为float *不会将底层数据更改为float但您的音频处理功能会将它们视为它们。然而,float and short值以完全不同的方式存储,因此您在该函数中所做的数学运算将在非常不同的值上进行操作,这些值与您的真实输入信号无关。要通过实验对此进行研究,请尝试以下操作:

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

The 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) is not sizeof(short)。其次,浮点数的“1 和 0”的存储方式与整数非常不同。看http://en.wikipedia.org/wiki/Single_ precision_floating-point_format 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尽管从您所写的内容来看,我想您不会喜欢后一种方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

iOS 6 中使用 AudioFileServices 进行粒度合成 的相关文章

随机推荐

  • Python通过字符串名称导入子模块?

    如何使用字符串列表 子模块名称 来导入当前模块中的子模块 当前代码 from mainapp utils import firstutil from mainapp utils import secondutil from mainapp
  • 防止 Google Play 上的虚假评论 [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我将 Android 应用程序发布到 Google Play 一切都很好 我收到了大约 5000 条用户评论 平均分为 4 6 分 但在某个时刻 我开
  • 如果 URL 参数很长,控制器操作不会调用

    仅供参考 我的问题不是重复的MVC 3 中的长 url 为 404 20 https stackoverflow com questions 20798392 404 20 for long url in mvc 3所以请不要混淆 我有一个
  • 谁在为kafka集群设置授权

    我有一个 3 节点 Kafka 集群和 2 个用于生产者和消费者的 kafka 客户端 我已启用 SSL 身份验证 我想为集群启用授权 我已在代理节点的 server properties 中添加了以下属性 authorizer class
  • 检测不同分辨率下的图像相等性

    我正在尝试构建一个脚本来浏览我的原始高分辨率照片 并替换我在拥有专业帐户之前上传到 Flickr 的旧的低分辨率照片 对于其中许多 我可以只使用 Exif 信息 例如拍摄日期 来确定匹配 但有些确实很旧 要么原始文件没有 Exif 信息 要
  • 如何使用java从linux环境获取tomcat中当前目录的相对路径

    我想用来从我的网络应用程序外部读取属性文件 我在 Windows 环境中的 tomcat 中部署了一个 war 文件 并且可以使用以下代码从 Web 应用程序外部读取属性文件 Method 1 String filePath new jav
  • Android OpenCV 并行化循环

    我知道 OpenMP 包含在 NDK 中 使用示例如下 http recursify com blog 2013 08 09 openmp on android http recursify com blog 2013 08 09 open
  • 通过转发构造函数参数构建基于可变参数模板的 mixin

    我正在尝试构建一个 mixin 模板 其基础全部作为可变参数模板参数传递 我想通过将每个 mixin 类的构造函数参数作为参数传递给可变参数模板构造函数来构造 mixin 当使用每个 mixin 类类型的对象调用时 可变参数模板构造函数会进
  • 在 Objective-C 中观察文件或文件夹

    侦听文件夹或文件以查看其是否已保存或是否已添加新文件的最佳方法是什么 如果您只想监视目录但不处理单个文件的监视 那么 FSEvents API 是理想的选择 Stu Connolly 有一个很棒的 FSEvents C API 的 Obje
  • 如何使用“%f”将双精度值填充到具有正确精度的字符串中

    我正在尝试使用 a 来填充带有双精度值的字符串sprintf像这样 sprintf S f val 但精度被截断至小数点后六位 我需要大约 10 位小数来保证精度 如何才能做到这一点 宽度 精度 宽度应包括小数点 8 2表示8个字符宽 点前
  • UIButton 在 UIScrollView 中时不起作用

    我的观点结构 UITableView UITableViewCell UIScrollView CustomView UIButton 问题是当我触摸 UIButton 时它不起作用 我用代码创建它 btn UIButton alloc i
  • 继续打开 OpenFileDialog 直到选择有效文件

    我有打开 OpenFileDialog 的代码 我正在检查文件的大小以确保它不超过特定限制 但是 如果用户选择了一个大尺寸的文件 我需要警告他并引导他返回对话框以选择不同的文件或单击 取消 这是我尝试过的 OpenFileDialog di
  • 获取 PHP 中动态选择的类常量的值

    我希望能够做这样的事情 class ThingIDs const Something 1 const AnotherThing 2 thing Something id ThingIDs thing 这是行不通的 有没有一种简单的方法可以做
  • 调试 Windows 消息内容和目标的好方法是什么?

    我正在开发一个基于其他行为模拟 Windows 鼠标的应用程序 一个示例是按键盘上的 或 键将 WM MOUSEWHEEL 消息发送到具有适当增量的目标窗口 问题是 在某些情况下 我很难复制那些消息i thinkwindows 正在发送到目
  • CUDA:如何检查计算能力是否正确?

    使用较高计算能力编译的 CUDA 代码将在计算能力较低的设备上完美执行很长一段时间 然后有一天在某些内核中默默地失败 我花了半天时间追寻一个难以捉摸的错误 结果发现构建规则已经sm 21而该设备 Tesla C2050 是2 0 是否有任何
  • 如何在 HTML 中打印每个项目之间有延迟的列表

    Id for each item p p p p p p
  • 如何在 Asp.Net-MVC 中添加自定义 HTTP 标头

    我创建了一个自定义处理程序 如下所示 public class SitHandler DelegatingHandler protected override async Task
  • facebook php,如何使用结果分页?

    您好 我正在使用 Facebook PHP SDK v 3 1 1 我不明白如何使用结果分页 url 我想获取我所有朋友的列表 这是我的代码 friends fb gt api me friends friend Array data gt
  • Invalid Uri : uri 方案无效

    我正在尝试通过 WebRequest 登录网站 我此时遇到异常 WebRequest req WebRequest Create formUrl Trim string url string username string password
  • iOS 6 中使用 AudioFileServices 进行粒度合成

    我对我正在开发的声音合成应用程序有疑问 我正在尝试读取音频文件 使用创建随机 颗粒 颗粒合成技术 http en wikipedia org wiki Granular synthesis 将它们放入输出缓冲区 然后能够使用 OpenAL