我正在使用一个应用程序,需要以60 ms的间隔播放声音“点击”(每分钟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];
提前感谢您的帮助......
答案 0 :(得分:2)
NSTimer(以及相关的NSThread睡眠)不是可靠的“时钟定时”机制。如果某些内容阻塞了主线程,则运行循环将在解除阻塞之前不会结束,并且不会按时调用定时器和线程休眠。
此外,关于NSTimer的分辨率最好约为1/30秒,尽管该框架没有对其射击的规律性做出任何承诺。如你所见,这不常见。如果你的邮件应用程序在后台启动,它可能会让你所有绚丽的嗡嗡声都被打乱。
我认为,您最好生成包含各种点击速度的较长声音文件,并且不那么频繁地触发它们。
答案 1 :(得分:0)
您应该尽可能避免[NSThread Sleep]。改为使用NSTimer:
[NSTimer scheduledTimerWithTimeInterval:(0.015) target:self selector:@selector(timerfn) userInfo:nil repeats:NO];
//the @selector(timerfn) is which function it calls
如果你想继续重放声音,在它调用的函数中调用这个计时器
OR
将计时器保存到如下变量:
mytimer=[NSTimer scheduledTimerWithTimeInterval:(0.015) target:self selector:@selector(timerfn) userInfo:nil repeats:YES];
并使用:
[mytimer invalidate];
mytimer=nil;
杀了它。
然后只播放功能中的声音。你也可以使用[NSTimer alloc]和[mytimer release]我认为......
另外,66.6秒的电话似乎有点矫枉过正。
答案 2 :(得分:0)
正如Dan Ray所说,NSTimer
和NSThread
对于精确计时并不可靠。如果我是你,我会使用audio queue - 它将通过输入回调请求音频缓冲区,你将填充它们。如果您及时回复,它将以完全硬件采样率请求和重现这些缓冲区,因此您可以进行一些简单的计算,以确定点击的时间并从文件中填充每个缓冲区或从存储在数组或向量中的样本。如果您选择直接从文件中删除,则使用AudioFileReadPackets
填写。
这将是一项更多的工作,但它更通用,更强大。您可以使用音频队列进行实时合成和处理音频,因此只要您满足其截止日期,它们的时间就非常精确。如果您对处理器的使用情况非常小心,那么每隔几毫秒就可以发出一个敲击声,没有问题。
查看SpeakHere示例应用程序以开始使用音频队列。 Audio units对于这类事情也很有用,你可以通过回调为他们提供数据,但是他们需要更多的设置并且学习曲线更陡峭。
祝你好运!