ãã®ãã¥ã¼ããªã¢ã«ã§ã¯ãé³ã®ä½æã¨å¤æ´ãæéã¨ã¹ã±ã¸ã¥ã¼ãªã³ã°ã«ã¤ãã¦è¿°ã¹ã¾ãããµã³ãã«ã®èªã¿è¾¼ã¿ãã¨ã³ããã¼ãããã£ã«ã¿ã¼ãã¦ã§ã¼ããã¼ãã«ã卿³¢æ°å¤èª¿ãç´¹ä»ãã¾ãããããã®ç¨èªã«æ £ãã¦ãã¦ãã¦ã§ããªã¼ãã£ãªAPIã«ããã¢ããªã±ã¼ã·ã§ã³ã®å ¥éãæ¢ãã¦ãããªããããªãã¯æ£ããå ´æã«æ¥ã¾ããã
ã¡ã¢: ä¸è¨ãã¢ã®ã½ã¼ã¹ã³ã¼ã㯠GitHub ã® MDN webaudio-examples ãªãã¸ããªã¼ã® step-sequencer ãµããã£ã¬ã¯ããªã¼ã«ããã¾ããã©ã¤ããã¢ãè¦ããã¨ãã§ãã¾ãã
ãã¢ããããã¨ã¦ãåç´ãªã¹ãããã·ã¼ã±ã³ãµã¼ãè¦ã¦ããã¾ãã
å®éã«ã¯ãã©ã¤ãã©ãªã¼ãä½¿ãæ¹ãç°¡åã§ããã¦ã§ããªã¼ãã£ãª API ã¯ããããåæã«æ§ç¯ããã¦ãã¾ãããã£ã¨è¤éãªãã®ãä½ããã¨ãã¦ããã®ã§ããã°ã tone.js ãå§ããã«ã¯æé©ãªå ´æã§ããããããããç§ãã¡ã¯å¦ç¿ã®ç·´ç¿ã¨ãã¦ãæåã®åçãããã®ãããªãã¢ã使ããæ¹æ³ã示ãããã¨æãã¾ãã
ã¤ã³ã¿ã¼ãã§ã¤ã¹ã¯ãã¹ã¿ã¼ã³ã³ããã¼ã©ã¼ã§æ§æããã¦ãããã·ã¼ã±ã³ãµã¼ãåç/忢ããããBPMï¼1ãåéã®ææ°ï¼ã調æ´ãã¦ã鳿¥½ããéããããé ãããããããã¨ãã§ãã¾ãã
4 ã¤ã®ç°ãªãé³ï¼ãã¤ã¹ï¼ãé³´ãããã¨ãã§ãã¾ããåãã¤ã¹ã«ã¯ 4 ã¤ã®ãã¿ã³ãããã鳿¥½ã® 1 å°ç¯ã®ããããã®æã« 1 ã¤ãã¤å¯¾å¿ãã¦ãã¾ããããããæå¹ã«ãªãã¨ãé³ç¬¦ãé³ã«ãªãã¾ããæ¥½å¨ãæ¼å¥ããã¨ããã®æã®éåãç§»åãããå°ç¯ãã«ã¼ããã¾ãã
åãã¤ã¹ã«ã¯ãã¼ã«ã«ã³ã³ããã¼ã«ãããããã¤ã¹ã使ããããã«ä½¿ç¨ããåãã¯ããã¯ç¹æã®ã¨ãã§ã¯ãã弿°ãæä½ãããã¨ãã§ãã¾ãã使ç¨ãã¦ããã¡ã½ããã¯ä»¥ä¸ã®éãã§ãã
ã¡ã¢: ç§ãã¡ããã®ã¤ã³ã¹ãã¥ã«ã¡ã³ãã使ããã®ã¯ãé³ãè¯ãããããã§ã¯ãªãããã¢ã³ã¼ããæä¾ããããã§ãããã®ãã¢ã¯ããã®ãããªã¤ã³ã¹ãã®é常ã«åç´åããããã¼ã¸ã§ã³ã表ãã¾ããé³ã¯ãã¤ã¢ã«ã¢ããã¢ãã ã«åºã¥ãã¦ãã¾ãããã®ãããªæ©å¨ãã©ã®ãããªé³ã§é³´ãã®ããåããªãæ¹ã¯ããã¡ãã§è©¦è´ã§ãã¾ãã
é³å£°ã³ã³ããã¹ãã®ä½æããã«ä½¿ç¨ãããã¨ã«ãªãã¾ãããã¦ã§ãé³å£° API ã¢ããªã¯é³å£°ã³ã³ããã¹ãããå§ã¾ãã¾ãã
const audioCtx = new AudioContext();
"sweep" â çºæ¯å¨ãå¨ææ³¢ãã¨ã³ããã¼ã
"sweep" é³ãã¤ã¾ããã¤ã¤ã«ã¢ããããæã«èãããæåã®é³ãå¼ã³åºãããã«ãé³ãçæããçºæ¯å¨ã使ãã¾ãã
OscillatorNode
ã§ãåºæ¬çãªæ³¢å½¢ï¼ãµã¤ã³æ³¢ãç©å½¢æ³¢ãä¸è§æ³¢ããã³ã®ãªæ³¢ï¼ã決ã¾ãã¾ããããããæ¢å®å¤ã§ã¯æ¨æºã®æ³¢å½¢ã使ç¨ãã代ããã«ã PeriodicWave
ã¤ã³ã¿ã¼ãã§ã¤ã¹ã¨æ³¢å½¢ãã¼ãã«ã«è¨å®ããå¤ã使ã£ã¦èªåèªèº«ã§ä½æãã¾ãããã®ã«ã¹ã¿ã æ³¢ãçºæ¯å¨ã§ä½¿ç¨ããã«ã¯ãPeriodicWave()
ã³ã³ã¹ãã©ã¯ã¿ã¼ã使ç¨ãã¾ãã
æåã«ãå¨ææ³¢ã使ãã¾ãããã®ããã«ã¯ã PeriodicWave()
ã³ã³ã¹ãã©ã¯ã¿ã¼ã«å®æ°ã¨èæ°ã®å¤ã渡ãå¿
è¦ãããã¾ãã
const wave = new PeriodicWave(audioCtx, {
real: wavetable.real,
imag: wavetable.imag,
});
ã¡ã¢: ãã®ä¾ã§ã¯ãæ³¢åãã¼ãã«ã¯é常ã«å¤ãã®å¤ããããããå¥åã® JavaScript ãã¡ã¤ã« (wavetable.js
) ã§ä¿æããã¦ãã¾ãã Google Chrome Labs ã®ã¦ã§ããªã¼ãã£ãª API ã®ä¾ã§å¾ãããæ³¢å½¢ãã¼ãã«ã®ãªãã¸ããªã¼ããåãã¾ããã
ããã§ OscillatorNode
ã使ãããã®æ³¢ã使ãããã®ã«è¨å®ãããã¨ãã§ãã¾ãã
function playSweep(time) {
const osc = new OscillatorNode(audioCtx, {
frequency: 380,
type: "custom",
periodicWave: wave,
});
osc.connect(audioCtx.destination);
osc.start(time);
osc.stop(time + 1);
}
ããã§é¢æ°ã«æå»ã®å¼æ°ã渡ãã¦ãã¾ãããããã¯å¾ã§æé¤ã®ã¹ã±ã¸ã¥ã¼ã«ãç«ã¦ãã®ã«ä½¿ç¨ãã¾ãã
æ¯å¹ ã®å¶å¾¡ããã¯ç´ æ´ããããã¨ã§ãããæ¯å¹ ã¨ã³ããã¼ããããã°ããã¨æãã¾ãããï¼åç´ãªãã®ã使ãã¦ãã¦ã§ããªã¼ãã£ãª API ã§ã¨ã³ããã¼ãã使ããã®ã«å¿ è¦ãªã¡ã½ããã使ç¨ãã¦ã¿ã¾ãããã
ã¨ã³ããã¼ãã«ã¢ã¿ãã¯ã¨ãªãªã¼ã¹ãããã¨ãã¾ããã¤ã³ã¿ã¼ãã§ã¤ã¹ã®ç¯å²å ¥åã使ã£ã¦ã¦ã¼ã¶ã¼ãããããå¶å¾¡ã§ããããã«ãã¾ãã
<label for="attack">Attack</label>
<input
name="attack"
id="attack"
type="range"
min="0"
max="1"
value="0.2"
step="0.1" />
<label for="release">Release</label>
<input
name="release"
id="release"
type="range"
min="0"
max="1"
value="0.5"
step="0.1" />
ããã§ JavaScript ã§å¤æ°ã使ããå ¥åå¤ãæ´æ°ãããã¨ãã«ãã®å¤æ°ã夿´ããããã«ãã¾ãã
let attackTime = 0.2;
const attackControl = document.querySelector("#attack");
attackControl.addEventListener(
"input",
(ev) => {
attackTime = parseInt(ev.target.value, 10);
},
false,
);
let releaseTime = 0.5;
const releaseControl = document.querySelector("#release");
releaseControl.addEventListener(
"input",
(ev) => {
releaseTime = parseInt(ev.target.value, 10);
},
false,
);
æçµç㪠playSweep() 颿°
ããã§ playSweep()
颿°ãå±éããããã¨ãã§ãã¾ããã GainNode
ã追å ãã¦ããããé³å£°ã°ã©ãã«æ¥ç¶ãã¦ãé³ã«æ¯å¹
ã®å¤åãå ããå¿
è¦ãããã¾ããã²ã¤ã³ãã¼ã㯠1 ã¤ã®ããããã£ãæã£ã¦ãã¾ãã gain
ã§ AudioParam
åã§ãã
ããã¯æç¨ãªãã¨ã§ããããã§ãã²ã¤ã³å¤ã«é¢ããé³å£°å¼æ°ã®ã¡ã½ããã®ãã¯ã¼ãå©ç¨ãå§ãããã¨ãã§ãã¾ããããæå»ã«å¤ãè¨å®ãããã¨ãã§ãã¾ããã AudioParam.linearRampToValueAtTime
ã®ãããªã¡ã½ããã§æéãå¤ã夿´ãããã¨ãã§ãã¾ãã
åè¿°ã®ããã«ãã¢ã¿ãã¯ã¨ãªãªã¼ã¹ã«ã¯ linearRampToValueAtTime
ã¡ã½ããã使ç¨ãã¾ãããã®ã¡ã½ããã«ã¯ 2 ã¤ã®å¼æ°ãåãã¾ããè¨å®ããã弿°ã®å¤ï¼ãã®å ´åã¯ã²ã¤ã³ï¼ã¨ããã¤è¨å®ãããã§ãããã®å ´åããã¤ãã¯å
¥åã³ã³ããã¼ã«ã§å¶å¾¡ãã¾ããä¾ãã°ä¸è¨ã®ä¾ã§ã¯ãã¢ã¿ãã¯ç¯å²ãå®ç¾©ããæå»ã®éãã²ã¤ã³ã¯ç´ç·çã« 1 ã«å¢å ãã¾ããåæ§ã«ããªãªã¼ã¹ã®å ´åãã²ã¤ã³ã¯ããªãªã¼ã¹å
¥åãè¨å®ããã¦ããæå»ã«ããã£ã¦ãç´ç·çãªå²åã§ 0 ã«éåãã¾ãã
const sweepLength = 2;
function playSweep(time) {
const osc = new OscillatorNode(audioCtx, {
frequency: 380,
type: "custom",
periodicWave: wave,
});
const sweepEnv = new GainNode(audioCtx);
sweepEnv.gain.cancelScheduledValues(time);
sweepEnv.gain.setValueAtTime(0, time);
sweepEnv.gain.linearRampToValueAtTime(1, time + attackTime);
sweepEnv.gain.linearRampToValueAtTime(0, time + sweepLength - releaseTime);
osc.connect(sweepEnv).connect(audioCtx.destination);
osc.start(time);
osc.stop(time + sweepLength);
}
"pulse" â ä½å¨æ³¢çºæ¯å¨å¤èª¿
ããã§ sweep ãã§ãã¾ãããæ¬¡ã«ç§»åãã¦ãç´ æµãªãã«ã¹é³ãè¦ã¦ã¿ã¾ããããåºæ¬çãªçºæ¯å¨ã§ã 2 ã¤ç®ã®çºæ¯å¨ã§å¤èª¿ãããã¨ã§å®ç¾ã§ãã¾ãã
åæçºæ¯å¨æåã® OscillatorNode
ã¯ã¹ã¤ã¼ãé³ã¨åãããã«è¨å®ãã¾ãããç¬èªã®æ³¢å½¢ãè¨å®ããããã®æ³¢å½¢ãã¼ãã«ã使ç¨ãã¾ããã
const osc = new OscillatorNode(audioCtx, {
type: "sine",
frequency: pulseHz,
});
ããã§ GainNode
ã使ãã¾ãã 2 ã¤ç®ã®ä½å¨æ³¢çºæ¯å¨ã§çºæ¯ããã gain
å¤ã§ãã
const amp = new GainNode(audioCtx, {
value: 1,
});
第äºã®ä½å¨æ³¢çºæ¯å¨ã®ä½æ
ããã§ãæåã®ãµã¤ã³æ³¢ã®å¢å¹ çãå¤ããããã«ã 2 ã¤ç®ã®ç©å½¢æ³¢ï¼ã¾ãã¯ãã«ã¹ï¼çºæ¯å¨ã使ãã¾ãã
const lfo = new OscillatorNode(audioCtx, {
type: "square",
frequency: 30,
});
ã°ã©ãã®æ¥ç¶
ããã§éè¦ãªã®ã¯ãã°ã©ããæ£ããæ¥ç¶ãããã¨ã¨ã両æ¹ã®çºæ¯å¨ãèµ·åãããã¨ã§ãã
lfo.connect(amp.gain);
osc.connect(amp).connect(audioCtx.destination);
lfo.start();
osc.start(time);
osc.stop(time + pulseTime);
ã¡ã¢: ã¾ãã使ããã©ã¡ãã®çºæ¯å¨ã«ãæ¢å®å¤ã®æ³¢å½¢ãã¼ãã«ã使ç¨ããå¿ è¦ã¯ããã¾ãããåã¨åãããã«æ³¢å½¢ãã¼ãã«ã¨å¨ææ³¢ã¡ã½ããã使ç¨ã§ãã¾ããæå°ã®ãã¼ãã§ãå¤ãã®å¯è½æ§ãããã¾ãã
ãã«ã¹ã®ã¦ã¼ã¶ã¼ã³ã³ããã¼ã«UI ã³ã³ããã¼ã«ã®ããã«ãçºæ¯å¨ã®ä¸¡æ¹ã®å¨æ³¢æ°ãå ¬éããç¯å²å ¥åã§å¶å¾¡ã§ããããã«ãã¾ãããã䏿¹ã¯é³ç¨ã夿´ãããã䏿¹ã¯ãã«ã¹ãæåã®æ³¢ãã©ã®ããã«å¤èª¿ãããã夿´ãã¾ãã
<label for="hz">Hz</label>
<input
name="hz"
id="hz"
type="range"
min="660"
max="1320"
value="880"
step="1" />
<label for="lfo">LFO</label>
<input name="lfo" id="lfo" type="range" min="20" max="40" value="30" step="1" />
ååã¨åæ§ã«ãã¦ã¼ã¶ã¼ã弿°ã®ç¯å²ã夿´ããã¨ãã«ã弿°ãå¤åããã¾ãã
let pulseHz = 880;
const hzControl = document.querySelector("#hz");
hzControl.addEventListener(
"input",
(ev) => {
pulseHz = parseInt(ev.target.value, 10);
},
false,
);
let lfoHz = 30;
const lfoControl = document.querySelector("#lfo");
lfoControl.addEventListener(
"input",
(ev) => {
lfoHz = parseInt(ev.target.value, 10);
},
false,
);
æçµç㪠playPulse() 颿°
playPulse()
颿°ã®å
¨ä½ã¯ä»¥ä¸ã®ããã«ãªãã¾ãã
const pulseTime = 1;
function playPulse(time) {
const osc = new OscillatorNode(audioCtx, {
type: "sine",
frequency: pulseHz,
});
const amp = new GainNode(audioCtx, {
value: 1,
});
const lfo = new OscillatorNode(audioCtx, {
type: "square",
frequency: lfoHz,
});
lfo.connect(amp.gain);
osc.connect(amp).connect(audioCtx.destination);
lfo.start();
osc.start(time);
osc.stop(time + pulseTime);
}
"noise" â ãã¤ã¯ã¯ãããã£ã«ã¿ã¼ä»ãã©ã³ãã ãã¤ãºãããã¡ã¼
ãã¤ãºãåºããã¨ãå¿ è¦ã«ãªãã¾ãããã¢ãã ã«ã¯ãã¹ã¦ãã¤ãºãããã¾ããé³å£°ãã¼ã¿ã®å ´åããã¤ãºã¯åãªãä¹±æ°ãªã®ã§ãã³ã¼ãã§ä½æããã®ã¯æ¯è¼çç°¡åãªãã¨ã§ãã
é³å£°ãããã¡ã¼ã®ä½æããããã¦ã§ããªã¼ãã£ãª API ãçè§£ã§ãã空ã®ã³ã³ããã¼ã使ããå¿
è¦ãããã¾ããããã§ AudioBuffer
ãªãã¸ã§ã¯ãã®åºçªã§ãããã¡ã¤ã«ãåå¾ãã¦ãããã¡ã¼ã«ãã³ã¼ããããã¨ãã§ãã¾ããï¼ãã¥ã¼ããªã¢ã«ã®å¾åã§èª¬æãã¾ãï¼ã空ã®ãããã¡ã¼ã使ãã¦ãã¼ã¿ãå
¥ãããã¨ãã§ãã¾ãã
ãã¤ãºã«ã¤ãã¦ã¯ãå¾è
ã§èª¬æãã¾ããæåã«ã使ãããããã¡ã¼ãµã¤ãºãè¨ç®ããå¿
è¦ãããã¾ããããã«ã¯ BaseAudioContext.sampleRate
ããããã£ã使ç¨ã§ãã¾ãã
const bufferSize = audioCtx.sampleRate * noiseDuration;
// 空ã®ãããã¡ã¼ã使
const noiseBuffer = new AudioBuffer({
length: bufferSize,
sampleRate: audioCtx.sampleRate,
});
ããã§ -1 ãã 1 ã®éã®ä¹±æ°ã§åãããã¨ãã§ãã¾ãã
// ãããã¡ã¼ã«ãã¤ãºãå
å¡«
const data = noiseBuffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
data[i] = Math.random() * 2 - 1;
}
ã¡ã¢: ãªã -1ï½1 ãªã®ã§ããããï¼ãã¡ã¤ã«ãã¹ãã¼ã«ã¼ã«é³ãåºåããå ´åã 0dB ãä¸éã®ã¹ã±ã¼ã«ï¼ã¡ãã£ã¢ã DAC ã®åºå®çãªæ°å¤ã®éçï¼ãè¡¨ãæ°å¤ãå¿ è¦ã§ããæµ®åå°æ°ç¹ã®é³å£°ã§ã¯ã 1 ã¯ä¿¡å·ã®æ°å¦çå¦çãããããã«ãä¸éãã«å²ãå½ã¦ããã便å©ãªæ°åã§ãããã®ãããçºæ¯å¨ããã¤ãºã¸ã§ãã¬ã¼ã¿ã¼ãããã¦ä»ã®é³æºã¯ãé常 -1 ãã 1 ã®ç¯å²ã®å極信å·ãåºåãã¾ãããã©ã¦ã¶ã¼ã¯ãã®ç¯å²å¤ã®å¤ãã¯ã©ã³ããã¾ãã
ãããã¡ã¼ã½ã¼ã¹ã®ä½æããã§é³å£°ãããã¡ã¼ãã§ãããã¼ã¿ãå
¥ãã¾ããããããã¡ã¼ãã½ã¼ã¹ã¨ãã¦ä½¿ç¨ã§ãããã¼ããã°ã©ãã«è¿½å ããå¿
è¦ãããã¾ãããã®ããã« AudioBufferSourceNode
ã使ãã使ãããã¼ã¿ã渡ãã¾ãã
// 使ãããã¼ã¿ã®ãããã¡ã¼ã½ã¼ã¹ã使
const noise = new AudioBufferSourceNode(audioCtx, {
buffer: noiseBuffer,
});
ãããé³å£°ã°ã©ãã«ã¤ãªãã§åçãã¾ãã
noise.connect(audioCtx.destination);
noise.start();
ããªããã¹ãã¤ãºã¨ããããã¡ãã¯ããªé³ã§ãããã¨ã«ãæ°ã¥ãã§ãããããã¯ã¤ããã¤ãºã使ããã®ã§ããããããããã¹ãã§ããå¤ã-1ãã1ã¾ã§åºãã£ã¦ãã¾ãããããã¯ãã¹ã¦ã®å¨æ³¢æ°ã«ãã¼ã¯ããããã¨ãæå³ãã¦ãã¾ãããã®é¢æ°ã0.5ãã-0.5ã¾ã§ã®å¤ã®ã¿ã«å¤æ´ãããã¼ã¯ãåãé¤ããä¸å¿«æã縮å°ãããã¨ãå¯è½ã§ãã使ãããã¤ãºããã£ã«ã¿ã¼ã«éãã¦ã¿ã¾ãããã
ãã¤ã¯ã¯ãããã£ã«ã¿ã¼ã追å ãã¦æ··åãã³ã¯ã¾ãã¯ãã©ã¦ã³ãã¤ãºã®ç¯å²ã«ãããã®ãå¿ è¦ã§ããé«ã卿³¢æ°ã¨åçºã®ä½ã卿³¢æ°ãã«ãããããã®ã§ãããã³ããã¹ãã¤ã¯ã¯ãããã£ã«ã¿ã¼ãé¸ã³ã¾ãããã
ã¡ã¢: ã¦ã§ããªã¼ãã£ãª API ã«ã¯ã BiquadFilterNode
㨠IIRFilterNode
ã® 2 ã¤ã®ç¨®é¡ã®ãã£ã«ã¿ãã¼ããããã¾ããã»ã¨ãã©ã®å ´åããã¤ã¯ã¯ãããã£ã«ã¿ã§ååã§ã - ãã¼ãã¹ããã¤ãã¹ããã³ããã¹ãªã©ã®æ§ã
ãªç¨®é¡ãããã¾ãããããããã£ã¨ç¹æ³¨ã®ãã®ãæ¢ãã¦ããã®ã§ããã°ã IIR ãã£ã«ã¿ã¼ãè¯ã鏿è¢ããããã¾ããã詳ãã㯠IIR ãã£ã«ã¿ã¼ã®ä½¿ç¨ãåç
§ãã¦ãã ããã
ãã®é
ç·ã¯ãåã«è¦ãã®ã¨åãã§ãã BiquadFilterNode
ã使ããå¿
è¦ãªããããã£ãè¨å®ããã°ã©ããéãã¦æ¥ç¶ãã¾ããä¾ãã°ããã³ããã¹åã§å¨æ³¢æ°ãè¨å®ããã«ã¯ãä¸é卿³¢æ°ã調æ´ãã¾ãããããããã¼ãã¹ã§ã¯ãä¸çªä¸ã®å¨æ³¢æ°ãè¨å®ãã¾ãã
// åºåãã£ã«ã¿ã¼
const bandpass = new BiquadFilterNode(audioCtx, {
type: "bandpass",
frequency: bandHz,
});
// ã°ã©ãã¸æ¥ç¶
noise.connect(bandpass).connect(audioCtx.destination);
ãã¤ãºã¦ã¼ã¶ã¼ã³ã³ããã¼ã«
UI ä¸ã§ã¯ãååã¨åãããã«ããã¤ãºã®ç¶ç¶æéã¨å¸¯åããã卿³¢æ°ãå ¬éããã¦ã¼ã¶ã¼ãç¯å²å ¥åã¨ã¤ãã³ããã³ãã©ã¼ã使ã£ã¦èª¿æ´ã§ããããã«ãã¾ãã
<label for="duration">Duration</label>
<input
name="duration"
id="duration"
type="range"
min="0"
max="2"
value="1"
step="0.1" />
<label for="band">Band</label>
<input
name="band"
id="band"
type="range"
min="400"
max="1200"
value="1000"
step="5" />
let noiseDuration = 1;
const durControl = document.querySelector("#duration");
durControl.addEventListener(
"input",
(ev) => {
noiseDuration = parseFloat(ev.target.value);
},
false,
);
let bandHz = 1000;
const bandControl = document.querySelector("#band");
bandControl.addEventListener(
"input",
(ev) => {
bandHz = parseInt(ev.target.value, 10);
},
false,
);
æçµç㪠playNoise() 颿°
playNoise()
颿°ã®å
¨ä½ã¯æ¬¡ã®ããã«ãªãã¾ãã
function playNoise(time) {
const bufferSize = audioCtx.sampleRate * noiseDuration; // set the time of the note
// Create an empty buffer
const noiseBuffer = new AudioBuffer({
length: bufferSize,
sampleRate: audioCtx.sampleRate,
});
// Fill the buffer with noise
const data = noiseBuffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
data[i] = Math.random() * 2 - 1;
}
// Create a buffer source for our created data
const noise = new AudioBufferSourceNode(audioCtx, {
buffer: noiseBuffer,
});
// Filter the output
const bandpass = new BiquadFilterNode(audioCtx, {
type: "bandpass",
frequency: bandHz,
});
// Connect our graph
noise.connect(bandpass).connect(audioCtx.destination);
noise.start(time);
}
"Dial-up" â é³ã®ä¾ã®èªã¿è¾¼ã¿
ããã¾ã§ä½¿ç¨ãã¦ããã¡ã½ããã使ã£ã¦ãããã¤ãã®çºæ¯å¨ãä¸ç·ã«é³´ãããã¨ã§ãé»è©±ã®ãã¤ã¤ã«é³ (DTMF) ãã¨ãã¥ã¬ã¼ããããã¨ã¯ãååã«ç°¡åã§ãã代ããã«ããã®ç¯ã§ã¯ãµã³ãã«ãã¡ã¤ã«ãèªã¿è¾¼ãã§ããã®å 容ãè¦ã¦ããã¾ãã
ãµã³ãã«ã®èªã¿è¾¼ã¿ä½¿ç¨ããåã«ãã¡ã¤ã«ãèªã¿è¾¼ã¾ãããããã¡ã¼ã«ãã³ã¼ãããããã¨ã確èªãããã®ã§ã async
颿°ã使ãã¦ãããã§ããããã«ãã¾ãããã
async function getFile(audioContext, filepath) {
const response = await fetch(filepath);
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
return audioBuffer;
}
ããã¦ããã®é¢æ°ãå¼ã³åºãã¨ãã« await
æ¼ç®åã使ããã¨ã§ãå®è¡ãå®äºããã¨ãã«å¾ç¶ã®ã³ã¼ãã確å®ã«å®è¡ãããã¨ãã§ãã¾ãã
ãµã³ãã«ãè¨å®ããããã«ãããä¸ã¤ async
颿°ã使ãã¾ãããããã®2ã¤ã®éåæé¢æ°ãç´ æµãªãããã¹ãã¿ã¼ã³ã§çµåãããã¨ã§ããã®ãã¡ã¤ã«ãèªã¿è¾¼ã¾ãã¦ãããã¡ã¼ãããã¨ãã«ãããªãã¢ã¯ã·ã§ã³ãå®è¡ãããã¨ãã§ãã¾ãã
async function setupSample() {
const filePath = "dtmf.mp3";
const sample = await getFile(audioCtx, filePath);
return sample;
}
ã¡ã¢: ä¸è¨ã®é¢æ°ãç°¡åã«å¤æ´ãããã¡ã¤ã«ã®é åãå¼ãç¶ãã§ã«ã¼ããããè¤æ°ã®ãµã³ãã«ãèªã¿è¾¼ããã¨ãã§ãã¾ãããã®ãã¯ããã¯ã¯ãããè¤éãªã¤ã³ã¹ãã¥ã«ã¡ã³ããã²ã¼ã ã«ä¾¿å©ã§ãããã
We can now use setupSample()
like so:
setupSample().then((sample) => {
// sample is our buffered file
// â¦
});
ãµã³ãã«ãåçããæºåãã§ããããããã°ã©ã ã« UI ãè¨å®ããã°ã使ããããã«ãªãã¾ãã
ãµã³ãã«ã®åçä»ã®é³ã¨åãããã«ã playSample()
颿°ã使ãã¾ããããä»å㯠AudioBufferSourceNode
ã使ããåå¾ããã³ã¼ããããããã¡ã¼ãã¼ã¿ãå
¥ãã¦åçãã¾ãï¼
function playSample(audioContext, audioBuffer, time) {
const sampleSource = new AudioBufferSourceNode(audioContext, {
buffer: audioBuffer,
playbackRate,
});
sampleSource.connect(audioContext.destination);
sampleSource.start(time);
return sampleSource;
}
ã¡ã¢: stop()
㯠AudioBufferSourceNode
ã§å¼ã³åºããã¨ãã§ãã¾ãããããã¯ãµã³ãã«ã®åçãå®äºããã¨ãã«èªåçã«è¡ããã¾ãã
AudioBufferSourceNode
ã«ã¯ playbackRate
ããããã£ãããã¾ãããã®ããããã£ã UI ã«å
¬éãããµã³ãã«ãéããããé
ããããã§ããããã«ãã¾ããããå
ã»ã©ã¨åããããªæ¹æ³ã§è¡ãã¾ãã
<label for="rate">Rate</label>
<input
name="rate"
id="rate"
type="range"
min="0.1"
max="2"
value="1"
step="0.1" />
let playbackRate = 1;
const rateControl = document.querySelector("#rate");
rateControl.addEventListener(
"input",
(ev) => {
playbackRate = parseInt(ev.target.value, 10);
},
false,
);
æçµç㪠playSample() 颿°
次㫠playSample()
颿°ã« playbackRate
ããããã£ãæ´æ°ããè¡ã追å ãã¾ããæçµçã«ã¯ãã®ããã«ãªãã¾ãã
function playSample(audioContext, audioBuffer, time) {
const sampleSource = new AudioBufferSourceNode(audioCtx, {
buffer: audioBuffer,
playbackRate,
});
sampleSource.connect(audioContext.destination);
sampleSource.start(time);
return sampleSource;
}
ã¡ã¢: ãã®é³å£°ãã¡ã¤ã«ã¯ soundbible.com ããå¼ç¨ãã¾ããã
æå»ã«åãããé³å£°ã®åçãã¸ã¿ã«é³å£°ã¢ããªã±ã¼ã·ã§ã³ã§ããããåé¡ã¯ããã¼ããä¸å®ã«ä¿ãããæå»ããããªãããã«é³ãåçãããã¨ã§ãã
for
ã«ã¼ãã®ä¸ã§åçããããã«é³è²ãã¹ã±ã¸ã¥ã¼ã«ãããã¨ãã§ãã¾ããããã®å ´åã®æå¤§ã®åé¡ã¯åçä¸ã«æ´æ°ãããã¨ã§ããã®ããã® UI ã³ã³ããã¼ã«ã¯ãã§ã«å®è£
ãã¦ãã¾ããã¾ããæ¥½å¨å
¨ä½ã® BPM ã³ã³ããã¼ã«ãæ¤è¨ããã®ãå®ã«ããã§ããããé³ç¬¦ããã¤æ¼å¥ãããããå
ã«è¦ã¦ãããããã¥ã¼ã«å
¥ããã®ã§ãã currentTime
ããããã£ã§æ£ç¢ºãªæå»ã«å§ãããã¨ãã§ãã夿´ãèãããã¨ãã§ãã¾ãã
ã¡ã¢: ãã®è¨äºã¯ã Chris Wilson's A Tale Of Two Clocks (2013) ã®è¨äºãå¤§å¹ ã«ç¸®å°ãããã®ã§ããã®ã¡ã½ããã«ã¤ãã¦ãã£ã¨è©³ããæ¸ããã¦ãã¾ããããã§ãã¹ã¦ãç¹°ãè¿ãæå³ã¯ããã¾ãããããã®è¨äºãèªãã§ãã®ã¡ã½ããã使ç¨ãããã¨ãå¼·ããå§ããã¾ããããã§ã®ã³ã¼ãã®å¤ãã¯å½¼ã®ã¡ãããã¼ã ã®ä¾ããå¼ç¨ãã¦ãã¾ããå½¼ã¯ãã®è¨äºã®ä¸ã§åç §ãã¦ãã¾ãã
æ¢å®ã® BPM (beats per minute) ãè¨å®ãããã¨ããå§ãã¾ãããããã® BPM ã¯ã¦ã¼ã¶ã¼ãå¶å¾¡ãããã¨ãã§ãã¾ãã
let tempo = 60.0;
const bpmControl = document.querySelector("#bpm");
bpmControl.addEventListener(
"input",
(ev) => {
tempo = parseInt(ev.target.value, 10);
},
false,
);
ããã¦ãã©ããããå ã¾ã§è¦ã¦ããã®ããã©ããããå ã®ã¹ã±ã¸ã¥ã¼ã«ãå®ç¾©ãã夿°ã使ãã¾ãã
const lookahead = 25.0; // How frequently to call scheduling function (in milliseconds)
const scheduleAheadTime = 0.1; // How far ahead to schedule audio (sec)
é³ç¬¦ã 1 æååã«ç§»åãã 4 çªç®ï¼æå¾ï¼ã®é³ç¬¦ã«å°éãããæåã®é³ç¬¦ã«ã«ã¼ãã§æ»ã颿°ã使ãã¦ã¿ã¾ãããã
let currentNote = 0;
let nextNoteTime = 0.0; // when the next note is due.
function nextNote() {
const secondsPerBeat = 60.0 / tempo;
nextNoteTime += secondsPerBeat; // Add beat length to last beat time
// Advance the beat number, wrap to zero when reaching 4
currentNote = (currentNote + 1) % 4;
}
æ¼å¥ããé³ç¬¦ã®åç §ãã¥ã¼ã¨ãåå使ãã颿°ã使ç¨ãã¦æ¼å¥ããæ©è½ã使ãããã¨æãã¾ãã
const notesInQueue = [];
function scheduleNote(beatNumber, time) {
// Push the note on the queue, even if we're not playing.
notesInQueue.push({ note: beatNumber, time });
if (pads[0].querySelectorAll("input")[beatNumber].checked) {
playSweep(time);
}
if (pads[1].querySelectorAll("input")[beatNumber].checked) {
playPulse(time);
}
if (pads[2].querySelectorAll("input")[beatNumber].checked) {
playNoise(time);
}
if (pads[3].querySelectorAll("input")[beatNumber].checked) {
playSample(audioCtx, dtmf, time);
}
}
ããã§ã¯ãç¾å¨ã®æå»ãè¦ã¦ã次ã®é³ç¬¦ã®æå»ã¨æ¯è¼ãã¾ãã両è ãä¸è´ããã¨ãååã¾ã§ã® 2 ã¤ã®é¢æ°ãå¼ã³åºãã¾ãã
AudioContext
ãªãã¸ã§ã¯ãã¤ã³ã¹ã¿ã³ã¹ã«ã¯ currentTime
ããããã£ããããæåã«ã³ã³ããã¹ãã使ãã¦ããä½ç§çµã£ãããåå¾ãããã¨ãã§ãã¾ããã¹ãããã·ã¼ã±ã³ãµã§ã¿ã¤ãã³ã°ãã¨ãããã«ä½¿ç¨ãã¾ããããã¯éå¸¸ã«æ£ç¢ºã§ãå°æ°ç¹ä»¥ä¸ 15 æ¡ç¨åº¦ã§æ£ç¢ºãªæµ®åå°æ°ç¹å¤ãè¿ãã¾ãã
let timerID;
function scheduler() {
// While there are notes that will need to play before the next interval,
// schedule them and advance the pointer.
while (nextNoteTime < audioCtx.currentTime + scheduleAheadTime) {
scheduleNote(currentNote, nextNoteTime);
nextNote();
}
timerID = setTimeout(scheduler, lookahead);
}
draw()
颿°ã UI ãæ´æ°ããããã«å¿
è¦ã§ãããããã°æåããã¤é²ãããè¦ããã¨ãã§ãã¾ãã
let lastNoteDrawn = 3;
function draw() {
let drawNote = lastNoteDrawn;
const currentTime = audioCtx.currentTime;
while (notesInQueue.length && notesInQueue[0].time < currentTime) {
drawNote = notesInQueue[0].note;
notesInQueue.shift(); // Remove note from queue
}
// We only need to draw if the note has moved.
if (lastNoteDrawn !== drawNote) {
pads.forEach((pad) => {
pad.children[lastNoteDrawn * 2].style.borderColor = "var(--black)";
pad.children[drawNote * 2].style.borderColor = "var(--yellow)";
});
lastNoteDrawn = drawNote;
}
// Set up to draw again
requestAnimationFrame(draw);
}
ãã¹ã¦ãç¨æãã
ããã§ããã¨ã¯ã¤ã³ã¹ãã¥ã«ã¡ã³ããæ¼å¥ããåã«ãµã³ãã«ãèªã¿è¾¼ãã ãã§ãããã¡ã¤ã«ãåå¾ããããã³ã¼ããããã¨æ¶ãããã¼ãã£ã³ã°ç»é¢ã追å ãã¾ããããã¦ããã¬ã¤ãã¿ã³ã®ã¯ãªãã¯ã¤ãã³ãã使ç¨ãã¦ã¹ã±ã¸ã¥ã¼ã©ã¼ãéå§ã§ããããã«ãã¾ãã
// When the sample has loaded, allow play
const loadingEl = document.querySelector(".loading");
const playButton = document.querySelector("#playBtn");
let isPlaying = false;
setupSample().then((sample) => {
loadingEl.style.display = "none";
dtmf = sample; // to be used in our playSample function
playButton.addEventListener("click", (ev) => {
isPlaying = !isPlaying;
if (isPlaying) {
// Start playing
// Check if context is in suspended state (autoplay policy)
if (audioCtx.state === "suspended") {
audioCtx.resume();
}
currentNote = 0;
nextNoteTime = audioCtx.currentTime;
scheduler(); // kick off scheduling
requestAnimationFrame(draw); // start the drawing loop.
ev.target.dataset.playing = "true";
} else {
clearTimeout(timerID);
ev.target.dataset.playing = "false";
}
});
});
ã¾ã¨ã
ããã§ãã©ã¦ã¶ã¼å ã«æ¥½å¨ãã§ãã¾ããããããã®ãã¯ããã¯ãçºå±ããã¦ããã£ã¨æã®è¾¼ãã ãã®ã使ãããã¨ãã§ãã¾ãã
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4