关于JavaScript实现MIDI信号不均等间隔发送的技术咨询
嘿,我明白你要做的事儿——在固定总时长内发送指定数量的MIDI信号,但要让间隔是递增或递减的,而不是均匀的。这其实是个典型的时序加权分配问题,我给你一套具体的实现思路和代码,你可以直接拿去用或者调整。
核心思路解析
首先,均匀间隔的逻辑很直接:间隔时长 = 总时长 / 信号数量。但非均匀模式下,我们需要给每个间隔分配一个「权重」——权重越大,间隔越长(递增模式下后面的权重更高,递减模式下前面的权重更高)。然后用总权重之和作为分母,每个间隔的实际时长就是 (总时长 / 总权重) * 该间隔权重,这样所有间隔加起来的总时长必然等于你设定的值,完美满足需求。
可复用代码实现
1. 生成非均匀间隔数组的函数
这个函数负责根据你指定的模式,计算出每个信号之间的等待时长:
// 生成非均匀间隔数组(单位:毫秒) function generateIntervals(totalTime, signalCount, mode = 'linear-increase') { const intervals = []; let totalWeight = 0; const weights = []; // 为每个间隔生成对应权重 for (let i = 1; i <= signalCount; i++) { let weight; switch(mode) { case 'linear-increase': weight = i; // 线性递增:第1个间隔权重1,第2个2,以此类推 break; case 'linear-decrease': weight = signalCount - i + 1; // 线性递减:第1个权重=信号数,最后一个=1 break; case 'quadratic-increase': weight = i ** 2; // 二次递增:间隔增长速度比线性更快 break; case 'quadratic-decrease': weight = (signalCount - i + 1) ** 2; // 二次递减 break; case 'exponential-increase': weight = Math.pow(2, i); // 指数递增,间隔增长极快 break; default: weight = 1; // 默认回退到均匀间隔 } weights.push(weight); totalWeight += weight; } // 将权重转换为实际时长 for (const weight of weights) { intervals.push((totalTime / totalWeight) * weight); } return intervals; }
2. 发送MIDI信号的时序控制
有了间隔数组后,我们可以用两种方式实现时序控制:一种是简单的setTimeout(适合对精度要求不高的场景),另一种是requestAnimationFrame(精度更高,适合音乐类对时序敏感的场景)。
方式一:使用setTimeout
// 模拟实际发送MIDI信号的函数(替换成你的Web MIDI API逻辑) function sendMidiSignal(signalIndex) { console.log(`发送第${signalIndex + 1}个MIDI信号`); // 示例:如果你用Web MIDI API,这里可以调用output.send([...]) } // 按非均匀间隔发送信号的主函数 function sendMidiSequence(totalTime, signalCount, mode) { const intervals = generateIntervals(totalTime, signalCount, mode); let currentDelay = 0; intervals.forEach((interval, index) => { setTimeout(() => { sendMidiSignal(index); }, currentDelay); currentDelay += interval; }); } // 调用示例:5秒内发送10个信号,采用线性递增模式 sendMidiSequence(5000, 10, 'linear-increase');
方式二:使用requestAnimationFrame(高精度)
function sendMidiSequenceRAF(totalTime, signalCount, mode) { const intervals = generateIntervals(totalTime, signalCount, mode); const startTime = performance.now(); let currentIndex = 0; let nextTriggerTime = intervals[0]; function checkTiming(currentTime) { if (currentIndex >= signalCount) return; const elapsed = currentTime - startTime; if (elapsed >= nextTriggerTime) { sendMidiSignal(currentIndex); currentIndex++; if (currentIndex < signalCount) { nextTriggerTime += intervals[currentIndex]; } } requestAnimationFrame(checkTiming); } requestAnimationFrame(checkTiming); } // 调用示例:3秒内发送8个信号,二次递减模式 sendMidiSequenceRAF(3000, 8, 'quadratic-decrease');
自定义扩展
如果你需要更个性化的间隔曲线(比如先慢后快再慢),只需要修改generateIntervals里的权重计算逻辑就行。比如你可以用正弦函数生成权重:
// 示例:先递增后递减的正弦曲线权重 case 'sine-curve': // 生成0到π的正弦值,映射为权重(先增后减) weight = Math.sin((i / signalCount) * Math.PI); break;
只要保证每个间隔的权重是正数,最终的总时长都会严格等于你设定的totalTime。
内容的提问来源于stack exchange,提问作者Matthew
相关产品推荐
相关产品推荐

