如何在没有噼啪声的情况下快速改变播放音调的频率?
How to fast change frequency of played tone without crackling?
我有 50-200FPS 的循环,想用声音可视化数据。我需要快速 流畅 响应数据变化,比如 Therminvox。
什么库和方法最适合这个?
simpleTones.js 的问题:
- 声音降级超过 ~2 分钟并最终停止工作
- 我没有找到如何改变频率,但不仅演奏单独的音符
Tone.js 的问题:
- 我不知道如何更改频率
document.onmousemove=init
bInit=false;
Timeout=100;
NoteLength=100;
function init() //this is not solving alert "The AudioContext was not allowed to start"
{
if(bInit) return;
//const osc = new Tone.Oscillator(440, "sine").toDestination().start();
setTimeout(loop, 200);
bInit=true;
}
function freqRand(){return 50+Math.random()*1000;}
function loop( )
{
playTone(freqRand(),'sine',NoteLength/1000)
setTimeout(loop, Timeout);
}
//--------UI
function sliderTimeout_change(v){
Timeout=v; document.getElementById('Timeout').innerText=v;
}
function sliderNoteLength_change(v){
NoteLength=v; document.getElementById('NoteLength').innerText=v;
}
<script src="https://cdn.jsdelivr.net/gh/escottalexander/simpleTones.js/simpleTones.js"></script>
<!-- <script src="Tone.js"></script> -->
<body>
Timeout = <i id='Timeout'></i><br>
<input id='sliderTimeout' type="range" min="10" max="200" value="100" oninput="sliderTimeout_change(this.value)"><br><br>
NoteLength = <i id='NoteLength'></i><br>
<input id='sliderNoteLength' type="range" min="10" max="4000" value="100" oninput="sliderNoteLength_change(this.value)">
</body>
下一个代码解决了任务,使用内置的 linearRampToValueAtTime 但最好有如何使用 Buffer 执行此操作的附加示例。
document.onclick=init //onmousemove is not solving alert "The AudioContext was not allowed to start"
bInit=false;
Timeout=100;
var osc;
var aContext;
function init()
{
if(bInit) return;
aContext = new(window.AudioContext || window.webkitAudioContext)();
osc = aContext.createOscillator();
osc.type = 'sine'; //sine square sawtooth triangle
var now = aContext.currentTime;
osc.frequency.setValueAtTime(440, now);
osc.connect(aContext.destination);
osc.frequency.setValueAtTime(440, now+1);
osc.start(now);
setTimeout(loop, 100);
bInit=true;
}
function loop( )
{
let freq=freqRand(); freq=smothFrequency(freq);
osc.frequency.linearRampToValueAtTime(freq,aContext.currentTime+Timeout/1000)
setTimeout(loop, Timeout);
}
function freqRand(){return 300+Math.random()*300+ Math.sin(aContext.currentTime/10)*250;}
//============================== end of main part, next is for playing
//--------signal conditioning, smooth
var freq_avg=0;
var freq_avg_k=0.1;
var freq_RangeMul=1;
function smothFrequency(freq_new)
{
freq_new*=freq_RangeMul;
if(freq_avg==0 || bNoSmooth) freq_avg=freq_new;
else //smooth freq change
{
if(Math.abs(freq_avg-freq_new)<Timeout/4) // no smooth when changes 4hz/ms
freq_avg=freq_avg;
else
{
if(Timeout>50) //less smooth for long timeout, or 'sharp'
{
freq_avg=freq_avg*(1-freq_avg_k*4)+freq_new*freq_avg_k*4;
if(freq_avg_k>=0.25)
{
if(bSharpKeepRange)
{
if(freq_avg<0) freq_avg=-freq_avg;
if(freq_avg>1600) freq_RangeMul/=1.05
else if(freq_avg<40) freq_RangeMul*=1.05
}
}
}
else
freq_avg=freq_avg*(1-freq_avg_k)+freq_new*freq_avg_k;
}
}
//console.log(aContext.currentTime, freq, freq_RangeMul);
return freq_avg;
}
//--------UI
var cc=null
function sliderTimeout_change(v){
cc=v;
v=v.value
Timeout=v; document.getElementById('Timeout').innerText=v;
}
function sliderNoteLength_change(v){
NoteLength=v; document.getElementById('NoteLength').innerText=v;
}
bNoSmooth=false;
function sliderfreq_avg_k_change(v){
freq_avg_k=v/1000;
bNoSmooth=(freq_avg_k>0.22 && freq_avg_k<0.33);
let str=freq_avg_k;
if(bNoSmooth)str='no smooth';
if(freq_avg_k>0.33)
{
document.getElementById('bSharpKeepRange').style.display='block';
str=str+' sharp';
}
else document.getElementById('bSharpKeepRange').style.display='none';
document.getElementById('freq_avg_k').innerText=str;
}
function checkbox_bSharpKeepRange_click(checkbox)
{
bSharpKeepRange=checkbox.checked
freq_avg=100;
freq_RangeMul=1;
}
bSharpKeepRange=false;
<body>
Timeout ms = <i id='Timeout'></i><br>
<input id='sliderTimeout' type="range" min="10" max="1000" value="10" oninput="sliderTimeout_change(this)"><br><br>
freq change smooth factor = <i id='freq_avg_k'></i><br>
<input id='sliderfreq_avg_k' type="range" min="2" max="550" value="10" oninput="sliderfreq_avg_k_change(this.value)"><br>
<i id="bSharpKeepRange">
keep range
<input id='checkbox_bSharpKeepRange' type="checkbox" onclick='checkbox_bSharpKeepRange_click(this);'>
</i>
</body>
我有 50-200FPS 的循环,想用声音可视化数据。我需要快速 流畅 响应数据变化,比如 Therminvox。 什么库和方法最适合这个?
simpleTones.js 的问题:
- 声音降级超过 ~2 分钟并最终停止工作
- 我没有找到如何改变频率,但不仅演奏单独的音符
Tone.js 的问题:
- 我不知道如何更改频率
document.onmousemove=init
bInit=false;
Timeout=100;
NoteLength=100;
function init() //this is not solving alert "The AudioContext was not allowed to start"
{
if(bInit) return;
//const osc = new Tone.Oscillator(440, "sine").toDestination().start();
setTimeout(loop, 200);
bInit=true;
}
function freqRand(){return 50+Math.random()*1000;}
function loop( )
{
playTone(freqRand(),'sine',NoteLength/1000)
setTimeout(loop, Timeout);
}
//--------UI
function sliderTimeout_change(v){
Timeout=v; document.getElementById('Timeout').innerText=v;
}
function sliderNoteLength_change(v){
NoteLength=v; document.getElementById('NoteLength').innerText=v;
}
<script src="https://cdn.jsdelivr.net/gh/escottalexander/simpleTones.js/simpleTones.js"></script>
<!-- <script src="Tone.js"></script> -->
<body>
Timeout = <i id='Timeout'></i><br>
<input id='sliderTimeout' type="range" min="10" max="200" value="100" oninput="sliderTimeout_change(this.value)"><br><br>
NoteLength = <i id='NoteLength'></i><br>
<input id='sliderNoteLength' type="range" min="10" max="4000" value="100" oninput="sliderNoteLength_change(this.value)">
</body>
下一个代码解决了任务,使用内置的 linearRampToValueAtTime 但最好有如何使用 Buffer 执行此操作的附加示例。
document.onclick=init //onmousemove is not solving alert "The AudioContext was not allowed to start"
bInit=false;
Timeout=100;
var osc;
var aContext;
function init()
{
if(bInit) return;
aContext = new(window.AudioContext || window.webkitAudioContext)();
osc = aContext.createOscillator();
osc.type = 'sine'; //sine square sawtooth triangle
var now = aContext.currentTime;
osc.frequency.setValueAtTime(440, now);
osc.connect(aContext.destination);
osc.frequency.setValueAtTime(440, now+1);
osc.start(now);
setTimeout(loop, 100);
bInit=true;
}
function loop( )
{
let freq=freqRand(); freq=smothFrequency(freq);
osc.frequency.linearRampToValueAtTime(freq,aContext.currentTime+Timeout/1000)
setTimeout(loop, Timeout);
}
function freqRand(){return 300+Math.random()*300+ Math.sin(aContext.currentTime/10)*250;}
//============================== end of main part, next is for playing
//--------signal conditioning, smooth
var freq_avg=0;
var freq_avg_k=0.1;
var freq_RangeMul=1;
function smothFrequency(freq_new)
{
freq_new*=freq_RangeMul;
if(freq_avg==0 || bNoSmooth) freq_avg=freq_new;
else //smooth freq change
{
if(Math.abs(freq_avg-freq_new)<Timeout/4) // no smooth when changes 4hz/ms
freq_avg=freq_avg;
else
{
if(Timeout>50) //less smooth for long timeout, or 'sharp'
{
freq_avg=freq_avg*(1-freq_avg_k*4)+freq_new*freq_avg_k*4;
if(freq_avg_k>=0.25)
{
if(bSharpKeepRange)
{
if(freq_avg<0) freq_avg=-freq_avg;
if(freq_avg>1600) freq_RangeMul/=1.05
else if(freq_avg<40) freq_RangeMul*=1.05
}
}
}
else
freq_avg=freq_avg*(1-freq_avg_k)+freq_new*freq_avg_k;
}
}
//console.log(aContext.currentTime, freq, freq_RangeMul);
return freq_avg;
}
//--------UI
var cc=null
function sliderTimeout_change(v){
cc=v;
v=v.value
Timeout=v; document.getElementById('Timeout').innerText=v;
}
function sliderNoteLength_change(v){
NoteLength=v; document.getElementById('NoteLength').innerText=v;
}
bNoSmooth=false;
function sliderfreq_avg_k_change(v){
freq_avg_k=v/1000;
bNoSmooth=(freq_avg_k>0.22 && freq_avg_k<0.33);
let str=freq_avg_k;
if(bNoSmooth)str='no smooth';
if(freq_avg_k>0.33)
{
document.getElementById('bSharpKeepRange').style.display='block';
str=str+' sharp';
}
else document.getElementById('bSharpKeepRange').style.display='none';
document.getElementById('freq_avg_k').innerText=str;
}
function checkbox_bSharpKeepRange_click(checkbox)
{
bSharpKeepRange=checkbox.checked
freq_avg=100;
freq_RangeMul=1;
}
bSharpKeepRange=false;
<body>
Timeout ms = <i id='Timeout'></i><br>
<input id='sliderTimeout' type="range" min="10" max="1000" value="10" oninput="sliderTimeout_change(this)"><br><br>
freq change smooth factor = <i id='freq_avg_k'></i><br>
<input id='sliderfreq_avg_k' type="range" min="2" max="550" value="10" oninput="sliderfreq_avg_k_change(this.value)"><br>
<i id="bSharpKeepRange">
keep range
<input id='checkbox_bSharpKeepRange' type="checkbox" onclick='checkbox_bSharpKeepRange_click(this);'>
</i>
</body>