【问题标题】:Accurate repeating iPhone sounds准确重复 iPhone 声音
【发布时间】:2011-08-22 15:31:20
【问题描述】:

我正在使用的应用程序需要以 60 毫秒的间隔播放声音“敲击”(每分钟敲击 1000 次)。当我超过大约 120 毫秒的间隔(每分钟 500 次敲击)时,声音变得非常不稳定,并且似乎相互叠加。

我使用的声音字节长 10 毫秒,我有一个以 20 毫秒间隔运行的线程计时器。我正在使用 AVAudioPlayer 播放声音,并尝试过 wav、caf 和 m4a 格式。我已经使用 NSLog 输出进行了测试,以查看 AVAudioPlayer 何时被触发,以及计时器循环何时轮询......并且所有时间似乎都足够准确。

我在 viewDidLoad() 方法中添加了音频会话处理,如下所示:

 [[AVAudioSession sharedInstance]
 setCategory: AVAudioSessionCategoryPlayback
 error: &setCategoryError];

这是我在自己的线程中运行的计时器循环:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

// Give the sound thread high priority to keep the timing steady.
[NSThread setThreadPriority:1.0];

while (running) {     // loop until stopped. 
    [self performSelectorOnMainThread:@selector(playOutput) withObject:nil waitUntilDone:NO];

    // An autorelease pool to prevent the build-up of temporary objects.
    NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];

    NSDate *curtainTime = [[NSDate alloc] initWithTimeIntervalSinceNow:sleepValue];
    NSDate *currentTime = [[NSDate alloc] init];
    // Wake up periodically to see if we've been cancelled. 
    while (running && ([currentTime compare:curtainTime] == NSOrderedAscending)) { 
        [NSThread sleepForTimeInterval:0.015];
        [currentTime release];
        currentTime = [[NSDate alloc] init];
    }

    [curtainTime release]; 
    [currentTime release]; 
    [loopPool drain];
}
[pool drain];

这是我初始化我的 AVAudioPlayer 的方式:

NSString *beatOne = @"3h";
NSString *beatOneSoundFile = [[NSBundle mainBundle]
                              pathForResource:beatOne ofType:@"caf"];
NSURL *beatOneURL = [[NSURL alloc] initFileURLWithPath:beatOneSoundFile];
beatPlayerOne = [[AVAudioPlayer alloc]initWithContentsOfURL:beatOneURL error:nil];
beatPlayerOne.delegate = self;
beatPlayerOne.currentTime = 0;
[beatPlayerOne prepareToPlay];
[beatOneURL release];

这里是播放音频播放器的地方:

[beatPlayerOne pause];
beatPlayerOne.currentTime = 0;
[beatPlayerOne play];

提前感谢您的帮助...

【问题讨论】:

  • 也许您可以考虑播放包含十个点击的音频文件?
  • 最终,点击之间的间隔将是可调的。它需要能够处理每分钟最多 1000 次点击(如果可能的话,更高),但最终,您会遇到等待时间太接近最小可识别时间量(即毫秒)的“量子”问题。

标签: iphone objective-c xcode avaudioplayer nsthread


【解决方案1】:

NSTimer(以及相关的 NSThread 睡眠)不是可靠的“时钟计时”机制。如果有什么东西阻塞了主线程,运行循环在它解除阻塞之前不会结束,并且不会按时调用定时器和线程休眠。

NSTimer 上的分辨率也处于最佳状态,大约为 1/30 秒,尽管该框架没有承诺其触发的规律性。如您所见,这不是常规的。如果您的邮件应用在后台启动,它可能会破坏您所有花哨的嗡嗡声。

我认为,您最好生成包含各种点击速度的较长声音文件,并减少触发频率。

【讨论】:

    【解决方案2】:

    正如 Dan Ray 所说,NSTimerNSThread 对于精确计时并不可靠。如果我是你,我会使用audio queue - 它会通过输入回调请求音频缓冲区,然后你将填充它们。如果您及时响应,它将以恰好硬件采样率请求和重现这些缓冲区,因此您可以进行一些简单的计算来计算您的点击时间并从您的文件或从存储在数组或向量中的样本。如果您选择直接从您的文件中填写,您可以使用 AudioFileReadPackets 填写。

    这将是一个多一点的工作,但它更加通用和强大。您可以使用音频队列进行音频的实时合成和处理,因此只要您在最后期限前完成,它们的时间安排就非常精确。如果您注意处理器的使用,您可以每隔几毫秒听到一次敲击声,没有问题。

    查看SpeakHere 示例应用程序以开始使用音频队列。 Audio units 对这类事情也很有用,您可以通过回调向它们提供数据,但它们需要更多设置并且学习曲线更陡峭。

    祝你好运!

    【讨论】:

    • 感谢所有好的意见。事实证明,越简单越好。我已经意识到 NSTimer 的缺点,所以我认为我的线程计时循环就足够了。
    【解决方案3】:

    您应该始终尽可能避免 [NSThread Sleep]。改用 NSTimer:

    [NSTimer scheduledTimerWithTimeInterval:(0.015) target:self selector:@selector(timerfn) userInfo:nil repeats:NO];
    //the @selector(timerfn) is which function it calls
    

    如果您想继续重放声音,请在它调用的函数中调用此计时器

    将计时器保存到一个变量中,如下所示:

     mytimer=[NSTimer scheduledTimerWithTimeInterval:(0.015) target:self selector:@selector(timerfn) userInfo:nil repeats:YES];
    

    并使用:

     [mytimer invalidate];
        mytimer=nil;
    

    杀死它。

    然后在函数中播放声音。你也可以使用 [NSTimer alloc] 和 [mytimer release] 我认为...

    另外,每秒 66.6 次调用似乎有点过头了。

    【讨论】:

    • 不幸的是,NSTimer 在精度方面非常不可靠。与其他语言(C++、Java 等)一样,线程可能非常有用,但您需要在内部处理精度级别。而且我认为 60 毫秒结果是每秒 16.66667 次点击。
    • 我不知道。谢谢!,我要离开 0.015 而不是 60 毫秒......所以都是正确的!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-08
    • 1970-01-01
    相关资源
    最近更新 更多