如何测量条件为真的持续时长?音乐家练习APP时间组件实现求助
为音乐家练习APP的光标移动逻辑添加时间维度方案
很高兴能帮你完善这款面向音乐家的练习APP!结合你已经实现的if(payedNote == highlightedNote){ cursor.next(); }核心逻辑,我整理了几个实用的技术方案,都是围绕测量匹配状态的持续时长来实现时间维度的控制,贴合音乐练习的实际需求:
方案一:基础计时跟踪(适合快速落地)
这个方案通过记录匹配状态的开始时间,实时计算持续时长,达到设定阈值后触发光标移动。
实现步骤:
- 初始化两个状态变量:
let currentMatchStartTime = null;// 记录匹配开始的时间戳let isMatching = false;// 标记当前是否处于音符匹配状态
- 在你的持续检测逻辑中加入时间判断:
// 假设这是你持续检测演奏音符的循环/回调函数 function checkNoteMatch(payedNote, highlightedNote) { const requiredDuration = getNoteDuration(highlightedNote); // 根据音符时值获取需要保持的时长(比如四分音符=500ms) if (payedNote === highlightedNote) { if (!isMatching) { // 首次匹配,开始计时 currentMatchStartTime = performance.now(); isMatching = true; } else { // 计算当前匹配持续时长 const elapsedTime = performance.now() - currentMatchStartTime; if (elapsedTime >= requiredDuration) { // 达到要求时长,移动光标 cursor.next(); // 重置计时状态,准备下一个音符 currentMatchStartTime = null; isMatching = false; } } } else { // 匹配中断,重置状态 currentMatchStartTime = null; isMatching = false; } } // 辅助函数:根据音符类型返回对应时长(需结合当前BPM计算) function getNoteDuration(note) { const bpm = 120; // 示例速度,实际从APP设置中获取 const quarterNoteMs = 60000 / bpm; // 四分音符的毫秒数 // 根据音符时值返回对应时长,比如八分音符是quarterNoteMs/2,二分音符是quarterNoteMs*2 switch(note.duration) { case 'quarter': return quarterNoteMs; case 'eighth': return quarterNoteMs / 2; case 'half': return quarterNoteMs * 2; default: return quarterNoteMs; } }
方案二:结合音乐节拍轴的精准控制(专业音乐场景推荐)
音乐的时间是基于节拍和速度的,单纯用绝对计时可能会和实际演奏节奏脱节。这个方案将光标移动和音乐节拍绑定,更符合音乐家的练习习惯。
实现思路:
- 提前根据乐谱的BPM和音符时值,生成每个音符的理论演奏时长和节拍位置。
- 启动一个和节拍同步的定时器(比如每拍触发一次),在每次节拍触发时检查:
- 当前演奏的音符是否和高亮音符匹配
- 匹配状态是否已经持续了至少该音符对应的节拍数(比如四分音符对应1拍,八分音符对应0.5拍)
- 当满足条件时,移动光标到下一个音符。
关键优势:
- 光标移动完全贴合音乐节奏,不会出现“快半拍/慢半拍”的情况
- 可以和APP内置的节拍器功能联动,让练习更规范
方案三:基于Web Audio API的高精度计时(音频输入场景)
如果你的APP是通过音频捕捉用户演奏的音符(而非MIDI输入),推荐使用Web Audio API的context.currentTime来计时,它的精度远高于系统的Date.now()或performance.now(),能避免音频处理的延迟误差。
核心代码片段:
const audioContext = new (window.AudioContext || window.webkitAudioContext)(); let matchStartAudioTime = null; function checkAudioNoteMatch(payedNote, highlightedNote) { const requiredDuration = getNoteDuration(highlightedNote); if (payedNote === highlightedNote) { if (!matchStartAudioTime) { matchStartAudioTime = audioContext.currentTime; } else { const elapsedTime = (audioContext.currentTime - matchStartAudioTime) * 1000; // 转成毫秒 if (elapsedTime >= requiredDuration) { cursor.next(); matchStartAudioTime = null; } } } else { matchStartAudioTime = null; } }
额外优化建议
- 容错机制:允许一定的时间误差(比如±100ms),避免用户因为微小的演奏偏差就触发状态重置
- 和弦处理:如果乐谱包含和弦,需要将匹配条件调整为
演奏的音符集合包含高亮的和弦音符集合,再进行时长判断 - 视觉反馈:在匹配过程中添加进度条或动态高亮效果,让用户直观看到当前音符的演奏时长是否达标
内容的提问来源于stack exchange,提问作者NclsK
相关产品推荐
相关产品推荐

