Swift 固体节拍器系统

2023-11-23

我正在尝试构建一个可靠的实体系统,以使用 SWIFT 在我的应用程序中构建节拍器。

到目前为止,我已经使用 NSTimer 构建了一个看似可靠的系统。我现在遇到的唯一问题是,当计时器启动时,前两次点击是关闭时间,但随后它会陷入一个可靠的时间范围。

现在,经过我的所有研究,我看到人们提到你应该使用其他不依赖 NSTimer 的音频工具。或者如果你选择使用 NSTimer 那么它应该在它自己的线程上。现在我看到很多人对此感到困惑,包括我自己,我很想深入了解节拍器业务的真相,解决这个问题,并与所有正在苦苦挣扎的人分享。

UPDATE

因此,在我上次收到反馈后,我已经实施并清理了这一点。此时,我的代码的结构如下。它正在播放。但一开始我仍然会快速点击两次,然后就稳定下来了。

我为我在这件事上的无知而道歉。我希望我走在正确的道路上。

我目前也在制作另一种方法的原型。我有一个非常小的音频文件,只需单击一下,其末尾就有死区,并具有正确的持续时间,直到达到特定节奏的循环点为止。我正在循环这个并且效果很好。但唯一的事情是我无法检测视觉更新的循环点,所以我的基本 NStimer 只是检测正在处理的音频下方的时间间隔,并且它似乎自始至终匹配得很好,没有延迟。但我还是宁愿用这个 NSTimer 来实现这一切。如果你能很容易地发现我的错误,那么在正确的方向上再踢一脚就太好了,我相信它很快就会起作用!非常感谢。

    //VARIABLES 
    //AUDIO
    var clickPlayer:AVAudioPlayer = AVAudioPlayer()
    let soundFileClick = NSBundle.mainBundle().pathForResource("metronomeClick", ofType: ".mp3")

    //TIMERS
    var metroTimer = NSTimer()
    var nextTimer = NSTimer()

    var previousClick = CFAbsoluteTimeGetCurrent()    //When Metro Starts Last Click


    //Metro Features
    var isOn            = false
    var bpm             = 60.0     //Tempo Used for beeps, calculated into time value
    var barNoteValue    = 4        //How Many Notes Per Bar (Set To Amount Of Hits Per Pattern)
    var noteInBar       = 0        //What Note You Are On In Bar


    //********* FUNCTIONS ***********

func startMetro()
{
     MetronomeCount()

    barNoteValue    = 4         // How Many Notes Per Bar (Set To Amount Of Hits Per Pattern)
    noteInBar       = 0         // What Note You Are On In Bar
    isOn            = true      //

        }

        //Main Metro Pulse Timer
        func MetronomeCount()
        {
            previousClick = CFAbsoluteTimeGetCurrent()

        metroTimer = NSTimer.scheduledTimerWithTimeInterval(60.0 / bpm, target: self, selector: Selector ("MetroClick"), userInfo: nil, repeats: true)

        nextTimer = NSTimer(timeInterval: (60.0/Double(bpm)) * 0.01, target: self, selector: "tick:", userInfo: ["bpm":bpm], repeats: true)
    }


    func MetroClick()
    {
        tick(nextTimer)
    }

    func tick(timer:NSTimer)
    {
        let elapsedTime:CFAbsoluteTime = CFAbsoluteTimeGetCurrent() - previousClick
        let targetTime:Double = 60/timer.userInfo!.objectForKey("bpm")!.doubleValue!
        if (elapsedTime > targetTime) || (abs(elapsedTime - targetTime) < 0.003)
        {
            previousClick = CFAbsoluteTimeGetCurrent()

            //Play the click here
            if noteInBar == barNoteValue
            {
                clickPlayer.play()    //Play Sound
                noteInBar = 1
            }
            else//If We Are Still On Same Bar
            {
                clickPlayer.play()    //Play Sound
                noteInBar++             //Increase Note Value
            }

            countLabel.text = String(noteInBar)     //Update UI Display To Show Note We Are At
        }

    }

纯粹用构建的节拍器NSTimer不会很准确,正如 Apple 在其文档中所解释的那样。

由于典型运行循环管理的输入源多种多样,因此定时器时间间隔的有效分辨率被限制在 50-100 毫秒的量级。如果计时器的触发时间发生在长标注期间或运行循环处于不监视计时器的模式时,则计时器不会触发,直到运行循环下次检查计时器为止。

我建议使用NSTimer每个所需滴答声会触发 50 次(例如,如果您想要每分钟 60 次滴答声,您将拥有NSTimeInterval约为 1/50 秒。

然后你应该存储一个CFAbsoluteTime它存储“最后一个刻度”时间,并将其与当前时间进行比较。如果当前时间和“最后一个刻度”时间之间的差异的绝对值小于某个容差(我会将其设置为每个间隔的刻度数的 4 倍,例如,如果您选择 1/50 秒每次 NSTimer 触发,您应该应用大约 4/50 秒的容差),您可以播放“滴答声”。

您可能需要校准容差才能达到所需的精度,但这个一般概念将使您的节拍器更加准确。

这里有一些更多信息另一个SO帖子。它还包括一些使用我讨论的理论的代码。我希望这有帮助!

Update您计算公差的方式不正确。在您的计算中,请注意容差与square的节拍数。这样做的问题是容差最终将小于计时器每秒触发的次数。看一眼这个图看看我的意思。这会在高 BPM 时产生问题。另一个潜在的错误来源是您的顶部边界条件。您确实不需要检查容忍度的上限,因为从理论上讲,计时器那时应该已经启动了。因此,如果经过的时间大于理论时间,你可以不管它。 (例如,如果经过的时间为 0.1 秒,而真实 BPM 的实际时间应为 0.05 秒,则无论您的容忍度如何,您都应该继续并触发计时器)。

这是我的计时器“滴答”功能,看起来工作正常。您需要对其进行调整以满足您的需求(包括悲观情绪等),但它在概念上是有效的。

func tick(timer:NSTimer) {
    let elapsedTime:CFAbsoluteTime = CFAbsoluteTimeGetCurrent() - lastTick
    let targetTime:Double = 60/timer.userInfo!.objectForKey("bpm")!.doubleValue!
    if (elapsedTime > targetTime) || (abs(elapsedTime - targetTime) < 0.003) {
        lastTick = CFAbsoluteTimeGetCurrent()  
        # Play the click here
    }
}

我的计时器初始化如下:nextTimer = NSTimer(timeInterval: (60.0/Double(bpm)) * 0.01, target: self, selector: "tick:", userInfo: ["bpm":bpm], repeats: true)

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

Swift 固体节拍器系统 的相关文章

随机推荐

  • org.apache.catalina.LifecycleException:子容器在启动期间失败

    SEVERE A child container failed during start java util concurrent ExecutionException org apache catalina LifecycleExcept
  • Spring - 无法解析 MVC“视图”thymeleaf

    我有一个简单的HomeController class package com example tacos import org springframework stereotype Controller import org spring
  • 获取 C 时区的夏令时转换日期

    在 C 中 是否有一种简单的跨平台方法来检索给定时区开始和结束夏令时的日期 我已经有了时区偏移信息以及当前是否正在观察夏令时 但我确实需要夏令时开始和结束的日期 对于我无法控制的外部依赖项 在 Windows 中 我正在使用获取时区信息 t
  • 在构造函数内分配原型

    我有这个代码 var MyClass function b this a b this getA function return that a var SecondClass function b this prototype new My
  • Multer 不接受数组格式的文件会出现“意外文件错误”

    Multer是与node js和express一起使用的用于上传文件的模块 我在角度方面使用 ng file upload 模块 当我一一发送多个文件时 它工作得很好 没有任何错误 但是当我以数组格式一次性发送所有文件 然后按照 Multe
  • Android ndk(cmake):在第二个 jni 库中使用日志 api 时,“未定义引用‘__android_log_write’”

    我使用 Android Studio 2 2 和 cmake 来构建 jni 文件 我想在 jni 文件中显示日志 但收到错误消息 未定义对 android log write 的引用 我的 CMakeLists txt 文件是 add l
  • CFG 的扩展,它是什么?

    考虑以下上下文无关语法的扩展 它允许规则在左侧有一个 或多个 终端在非终端的右侧 即 形式规则 A b gt 右侧可以是任何东西 就像在上下文无关语法中一样 特别是 它是not要求右侧末尾具有完全相同的终端符号 在这种情况下 此扩展将是上下
  • 大多数签名/未签名警告的可接受修复?

    我本人确信 在我正在研究的项目中 有符号整数在大多数情况下是最佳选择 即使其中包含的值永远不会是负数 更简单的反向 for 循环 更少的错误机会等 特别是对于只能保存 0 到 20 之间的值的整数 大多数出错的地方是 std vector
  • Android Play Places 出现 PLACES_API_INVALID_APP 错误

    我正在尝试 Google Place Autocomplete 示例https github com googlesamples android play places 我已将正确的 API 密钥放在正确的位置 我知道这一点是因为当我输入随
  • 关于:下载文件时浏览器中出现空白#被阻止

    我正在尝试从本地服务器下载文件 也许将来我需要在其他服务器上执行此操作 当我单击链接下载浏览器中的文件时 会出现此消息 about blank blocked 我正在使用 axios 和vue js 代码是这样的 downloadItem
  • Flex:如何检测用户是否阻止共享对象写入

    简单的问题是 如果用户已阻止将数据写入共享对象 我如何在动作脚本中检测 sharedObj SharedObject getLocal rememberme 这个返回总是共享对象 但它的大小是0 即使我已经阻止了共享对象 当我尝试将数据保存
  • 如何将多个python文件转换为EXE?

    我正在尝试将我的 python 应用程序转换为 exe 我见过像 py2exe 和 cx freeze 这样的东西 但它们只编译一个 py 文件 谁能帮我 谢谢 我目前使用py安装程序用于将项目构建为单个可执行文件 这些项目都包含多个Pyt
  • gacutil 构建后事件已退出,代码为 1

    我正在尝试在 GAC 中注册并组装构建后活动 这是我的构建后命令 C Program Files x86 Microsoft SDKs Windows v7 0A Bin NETFX 4 0 Tools x64 gacutil exe if
  • 如何在主目录中创建文件夹?

    我想创建一个目录path HOME somedir 我尝试过使用boost filesystem create directory path 但它失败了 显然该函数不会扩展系统变量 我怎样才能以最简单的方式做到这一点 注意 在我的例子中 字
  • 从单个项目源构建多个 jar

    我对 gradle 还很陌生 我想知道是否可以从同一项目源构建多个 jar 我浏览过以前的类似问题 但我的情况有点不同 我想将所有源文件编译两次以生成两个不同的 jar 每个 jar 具有不同的依赖项和不同的工件 id 从概念上讲 它可以表
  • MongoDB中每条记录的更新时间

    MongoDB 的 ObjectId 有一个 getTimestamp 方法 该方法返回插入记录的时间 是否有类似的方法来获取上次更新的时间 或者我应该使用单独的列来保存此数据 使用 PHP 如果重要的话 MongoDB 不存储最后的更新
  • 使用 jQuery 检查页面加载时的所有复选框

    在页面加载时 使用 jQuery 如何自动选择特定 div 中的所有复选框 function thediv input checkbox attr checked checked
  • 如何在war文件中运行java类文件[重复]

    这个问题在这里已经有答案了 我有一个名为 Sample war 的战争文件 在这个战争文件中 我有一个名为 Maintest 的主类 我想从战争之外运行或执行Maintest类 如何做到这一点 War 文件只是带有额外元数据的 Jar 文件
  • R:在函数内使用 dplyr。 eval(expr, envir, enclos) 中的异常:未知列

    我在 R 中创建了一个函数友善的帮助 Jim M 当我运行该函数时 我收到错误 错误 未知列 原始数据 当查看调试器时 我收到消息 Rcpp eval expr envir enclos 中的异常 未知列 rawdata 然而 当我查看环境
  • Swift 固体节拍器系统

    我正在尝试构建一个可靠的实体系统 以使用 SWIFT 在我的应用程序中构建节拍器 到目前为止 我已经使用 NSTimer 构建了一个看似可靠的系统 我现在遇到的唯一问题是 当计时器启动时 前两次点击是关闭时间 但随后它会陷入一个可靠的时间范