首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >网络音频时钟:使频率听起来正确

网络音频时钟:使频率听起来正确
EN

Stack Overflow用户
提问于 2021-04-12 14:40:23
回答 1查看 162关注 0票数 0

试图在JS创造一个单一的振荡器,一个平滑的,不点击频率的色调,迅速打开和关闭。由克里斯华莱士讨论的网络音频的时间。时间调度程序可以找到阿基拉米舍尔。这个例子根据频率产生一个正确的音调.但是,出现了一个明显的单击。已通过堆栈溢出上的公告解决了固定单击的问题。

第一个代码片段是基于代码笔版本(参见链接2),将频率更改为432 Hz。

第二个代码片段:从这些资源中改编的JS (参见链接2和3),添加倾斜,使用432 Hz频率,我可以让点击停止,但我的频率声音现在变钝了,不再有正确的432 Hz频率的声音。我怀疑我做错了什么,没有正确地使用时间或设置。我尝试过不同的时间和设置,甚至是“初始”而不是“指数”倾斜,但没有人解决了这个问题:单击“消失了”,但频率声音现在变钝了,根据选择的频率不能保留正确的声音,如果不使用倾斜,就会听到声音,留下点击。我一定做错什么了?

有办法在不减弱声音的情况下修复点击吗? 1:https://www.html5rocks.com/en/tutorials/audio/scheduling/ 2:https://codepen.io/aqilahmisuary/pen/ONEKVM 3:即使在使用exponentialRampToValueAtTime时,网络音频“单击”声音

代码语言:javascript
复制
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>

</head>

<body>
<p><button name="button" id="startBtn">Start</button>
  <button name="button" id="stopBtn">Stop</button></p>
<p>Audio Context Current Time:</p>
<p><span id="clock"></span></p>
<p>nextNotetime:</p>
<p><span id="nextNote"></span></p>

<style>
span, p, button {
    font-family: 'Courier New', Courier, 'Lucida Sans Typewriter', 'Lucida Typewriter', monospace;
    font-size: 25px;
    font-weight: 1000;
    line-height: 26.4px;
    text-align: center;
}

span {
    font-weight: 200;
}
</style>

<script>
window.AudioContext = window.AudioContext || window.webkitAudioContext;

var audioContext = new AudioContext();
var nextNotetime = audioContext.currentTime;
var clock = document.getElementById("clock");
var nextNote = document.getElementById("nextNote");
var startBtn = document.getElementById("startBtn");
var stopBtn = document.getElementById("stopBtn");
var timerID;

setInterval(function(){ clock.innerHTML = audioContext.currentTime; }, 100);

function playSound(time) {
  
  var osc = audioContext.createOscillator();
  osc.connect(audioContext.destination);
  osc.frequency.value = 432;
  osc.start(time);
  osc.stop(time + 0.1);
  
};

function scheduler() {

    while(nextNotetime < audioContext.currentTime + 0.1) {
        
        nextNotetime += 0.5;
        nextNote.innerHTML = nextNotetime;
        playSound(nextNotetime);
    }

   timerID = window.setTimeout(scheduler, 50.0);
}

startBtn.addEventListener('click', function() {

    scheduler();

  }, false);

stopBtn.addEventListener('click', function() {

    clearTimeout(timerID);

  }, false);

if(audioContext.state === 'suspended'){
  audioContext.resume();
};
</script>

</body>
</html>

代码语言:javascript
复制
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>
<body>
<p><button name="button" id="startBtn">Start</button>
  <button name="button" id="stopBtn">Stop</button></p>
<p>Audio Context Current Time:</p>
<p><span id="clock"></span></p>
<p>nextNotetime:</p>
<p><span id="nextNote"></span></p>
<style>
span, p, button {
    font-family: 'Courier New', Courier, 'Lucida Sans Typewriter', 'Lucida Typewriter', monospace;
    font-size: 25px;
    font-weight: 1000;
    line-height: 26.4px;
    text-align: center;
}
span {
    font-weight: 200;
}
</style>
<script>
window.AudioContext = window.AudioContext || window.webkitAudioContext;

var audioContext = new AudioContext();
var nextNotetime = audioContext.currentTime;
var clock = document.getElementById("clock");
var nextNote = document.getElementById("nextNote");
var startBtn = document.getElementById("startBtn");
var stopBtn = document.getElementById("stopBtn");
var timerID;
var gainNode = audioContext.createGain();
var osc;
var rampDuration = 0.3;
gainNode.connect(audioContext.destination);
setInterval(function(){ clock.innerHTML = audioContext.currentTime; }, 100);
function playSound(time) {  
  osc = audioContext.createOscillator();
  osc.connect(gainNode);
  osc.frequency.value = 432;
  osc.type = "sine";
  gainNode.gain.setValueAtTime(1, audioContext.currentTime);
  gainNode.gain.linearRampToValueAtTime(0.0001, audioContext.currentTime + rampDuration);
  gainNode.gain.linearRampToValueAtTime(1, audioContext.currentTime + rampDuration);
  osc.start(time);
  osc.stop(time + 0.01);  
};
function scheduler() {
    while(nextNotetime < audioContext.currentTime + 0.1) {        
        nextNotetime += 0.5;
        nextNote.innerHTML = nextNotetime;
        playSound(nextNotetime);
    }
   timerID = window.setTimeout(scheduler, 50.0);
}
startBtn.addEventListener('click', function() {
    scheduler();
  }, false);
stopBtn.addEventListener('click', function() {
    clearTimeout(timerID);
  }, false);
if(audioContext.state === 'suspended'){
  audioContext.resume();
};
</script>
</body>
</html>

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-04-12 16:11:27

根据这些评论,我们实际上是在处理一个需要精确计时的简短的重复循环,因此AudioBufferSourceNode是我们选择的武器:

它特别适用于播放具有特别严格的计时精度要求的音频,例如必须匹配特定节奏的声音。

不幸的是,这也意味着我们需要弄脏我们的手,编写一些DSP代码来合成这个缓冲区,但老实说,它并没有那么糟糕(特别是因为我们可以只使用浮点数,而不是原始的PCM缓冲区.)。

为了避免点击(振荡器在中间相位被切断),我们利用正弦波总是从零开始的事实,我们呈现出一个声音的单圈,所以我们所需要做的就是确保波的结束不会突然停止。我们通过稍微调整音调的长度来实现这一点,以确保最后一个可听到的样本非常接近于零。

这里的示例有几个按钮来演示不同的参数。您可以将它们连接到UI组件中。

代码语言:javascript
复制
var audioContext = new AudioContext();
var gainNode = audioContext.createGain();
gainNode.connect(audioContext.destination);
var playerNode = null; // Initialized later.

function createLoop(audioContext, toneFrequency, toneDuration, loopDuration) {
  const arrayBuffer = audioContext.createBuffer(
    1,
    audioContext.sampleRate * loopDuration,
    audioContext.sampleRate,
  );
  const channel = arrayBuffer.getChannelData(0); // mono, only deal with single channel
  const toneDurationInSamples = Math.floor(audioContext.sampleRate * toneDuration);
  const phasePerSample = (Math.PI * 2 * toneFrequency) / audioContext.sampleRate;
  let audible = true;
  for (let i = 0; i < arrayBuffer.length; i++) {
    if (audible) {
      let value = Math.sin(phasePerSample * i);
      channel[i] = value;
      // We might slightly overshoot the tone's requested duration
      // but we need to wait for the oscillation to be near zero
      // to avoid an audible click (when the signal "snaps" from an arbitrary
      // value to zero).
      if (i >= toneDurationInSamples && Math.abs(value) < 0.02) {
        audible = false;
      }
    } else {
      channel[i] = 0; // Silence
    }
  }
  return arrayBuffer;
}

function go(hz, length) {
  halt(); // Remove the old player node. We couldn't modify the buffer anyway.
  playerNode = audioContext.createBufferSource();
  playerNode.loop = true;
  playerNode.connect(gainNode);

  const buf = createLoop(audioContext, hz, length / 5, length);
  playerNode.buffer = buf;
  playerNode.start();
  audioContext.resume();
}

function halt() {
  if (playerNode) {
    playerNode.stop();
    playerNode.disconnect();
    playerNode = null;
  }
}
function handleVolumeChange(volume) {
  gainNode.gain.setValueAtTime(volume, audioContext.currentTime);
}
代码语言:javascript
复制
Since an Audio Context can't start with interaction, you'll need to hit a button...

<br />

<button onclick="go(432, 0.5)">Go at 432 hz</button>
<button onclick="go(880, 0.3)">Go faster and at 880 hz</button>
<button onclick="go(1250, 0.1)">Go really fast and high</button>
<button onclick="halt()">Stop going</button>

<br />

Volume: <input type="range" min="0" max="1" value="1" step="0.01" onInput="handleVolumeChange(event.target.valueAsNumber)">

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67060431

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档