简单的节拍器

时间:2011-09-16 12:28:32

标签: iphone objective-c ios grand-central-dispatch

我试图通过每0.25秒播放一次系统声音来编写一个简单的节拍器。我使用GCD来播放单独一个帖子上的点击,但是播放是不均匀的,点击有时是两个快节拍,然后是慢节拍。我记录了循环中if语句执行的时间,以及0.25秒的权限。我希望我不必使用音频队列服务。有什么建议吗?

- (IBAction)start:(id)sender 
{
    dispatch_queue_t clickQueue; // the queue to run the metronome clicker
    dispatch_queue_t mainQueue; // I access the main queue to demonstrate how to change UIKit items
    //clickQueue = dispatch_queue_create("clickQueue", NULL);
    clickQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    mainQueue = dispatch_get_main_queue();
    dispatch_async(clickQueue, ^{
        double timeWas = [NSDate timeIntervalSinceReferenceDate];
        //delay by a 1/10 of a second so the first few clicks don't bunch up.
        double timeIs = [NSDate timeIntervalSinceReferenceDate]  - 0.1; 
        // playing starts out as NO because it gets switched at the end of the loop
        // and the PlaySystemSound block isn't off the queue yet. There is probably a
        // better way to do this.
        while (playing) {
            timeIs = [NSDate timeIntervalSinceReferenceDate] ;
            if ((timeIs - timeWas) > (60.0/240)) {
                AudioServicesPlaySystemSound(sound);
                timeWas = timeIs;
                // I want to flast the 200 label between orange and black but I have to access
                // user interface objects from the queue that they are running in, usually the
                // main queue.
                dispatch_async(mainQueue, ^{
                    if (flash)
                        [bpm setTextColor:[UIColor orangeColor]];
                    else
                        [bpm setTextColor:[UIColor blackColor]];
                    flash = !flash;
                });
            }
        }
    });
    playing = !playing;
    if (playing) 
        [startButton setTitle:@"Stop" forState:UIControlStateNormal];
    else
        [startButton setTitle:@"Start" forState:UIControlStateNormal];
}

5 个答案:

答案 0 :(得分:3)

当时使用NSTimer,声音使用AVFoundation。

答案 1 :(得分:1)

http://www.metronomeonline.com/放在一起的人们在处理时序问题方面做得非常好,尽管他们没有针对任何特定的硬件/操作系统进行开发。我相信他们这样做的方法是为每个节奏创建预先录制的.wav / .mp3文件几秒钟,然后循环播放。循环速率计算为速度准确。通过使循环事件成为唯一依赖于客户端时序的事件,它们可以减少时序错误。

答案 2 :(得分:0)

更好的方法是使用具有所需间隔的重复NSTimer。你的方法会搅动CPU。

答案 3 :(得分:0)

我用我的节拍器应用程序自己一路走下同一条路线。不幸的是,使用像PlaySystemSound这样的高级API,你永远无法获得所需的精度 - 它们的反应速度不够快。

很可能,如果你想要你的节奏真正的精确度和你作为“点击”播放的声音,你会想要使用音频单元 - 你可能能够摆脱音频队列服务 - 我没有我没有多尝试,意识到无论如何我都需要在某个时候达到低水平,并选择全力以赴。

答案 4 :(得分:0)

我会尝试使用:

void dispatch_after(
   dispatch_time_t when,
   dispatch_queue_t queue,
   dispatch_block_t block);

您还可以阅读http://atastypixel.com/blog/experiments-with-precise-timing-in-ios/,这在您的背景中非常有趣......