音频上下文演示。在 Chrome/Android 上音量减半,在 Firefox 上噼啪作响
Audiocontext demo. Volume is halved on Chrome/Android, crackles on Firefox
我使用 AudioContext
对象做了一个纯 CSS/JS 钢琴键盘,但是我有两个与 playTone
函数相关的问题:
在Chrome/android (v.89.0.4389.105/Android 10
) 似乎每按一个键音量就会减半:弹奏几个音符后音量就听不见了。
在 Firefox (v.88/MacOS 10.15.7
) 上,我听到每个按键结束时都发出噼啪声。
最新的Chrome/MacOS,为了比较,听起来不错。
const noteFrequencies = {
'C1' : 261.63,
'C1#': 277.18,
'D1' : 293.66,
'D1#': 311.13,
'E1' : 329.63,
'F1' : 349.23,
'F1#': 369.99,
'G1' : 392.00,
'G1#': 415.30,
'A1' : 440.00,
'A1#': 466.16,
'B1' : 493.88
}
function playTone(note, duration = 1) {
let ac = new AudioContext();
let oscl = ac.createOscillator();
let gain = ac.createGain();
gain.connect(ac.destination);
oscl.connect(gain);
oscl.frequency.value = noteFrequencies[note];
oscl.type = "sine"
gain.gain.linearRampToValueAtTime(.0001, ac.currentTime + duration);
oscl.start();
oscl.stop(ac.currentTime + duration);
}
let notelist = document.querySelector('.piano');
notelist.addEventListener('click', (ev) => {
let tgt = ev.target;
let tone;
if (tgt.matches('[data-tone]')) {
tone = tgt.getAttribute('data-tone');
playTone(tone);
}
});
* {
box-sizing: border-box;
font-family: "Lobster Two";
color: #555666;
}
h1 {
text-align: center;
font-size: 5rem;
margin-bottom: 4rem;
}
.piano {
position : relative;
display : flex;
width : max-content;
list-style : none;
margin : 0 auto;
padding : 0;
align-items: flex-start;
}
.piano li {
--ar : .2;
display: inherit;
cursor : pointer;
color : transparent;
user-select : none;
aspect-ratio: var(--ar);
}
@supports (not (aspect-ratio: 1)) {
.piano li::before {
content: "";
padding-top: calc(100% / var(--ar));
}
}
li:not(.diesis) {
width : max(50px, 4vw);
border-left : 1px solid #c4c4c8;
border-bottom : 1px solid #c4c4c8;
border-radius : 0 0 4px 4px;
background : linear-gradient(to bottom, #f2f2f5, #fff);
box-shadow : 0 0 5px #ccc inset;
}
li:not(.diesis):active {
border-top : 1px solid #bbb;
border-left : 1px solid #ccc;
border-bottom : 1px solid #ccc;
background : linear-gradient(to bottom, #fff, #f2f2f5);
box-shadow :
2px 0 3px rgba(0,0,0,0.1) inset,
-4px 2px 10px rgba(0,0,0,.02) inset;
}
li.diesis {
position : relative;
z-index : 1;
--w : max(30px, 2.5vw);
width : var(--w);
margin : 0 calc(var(--w) / -2);
border : 1px solid #131313;
border-radius : 0 0 4px 4px;
background : linear-gradient(40deg, #222, #555);
box-shadow :
0 -1px 1px 2px rgba(0,0,0, .5) inset,
0 1px 2px rgba(0,0,0, .5);
}
li.diesis:active {
background: linear-gradient(100deg, #505050, #131313);
box-shadow:
-1px -1px 2px rgba(255,255,255,0.2) inset,
0 -2px 2px 3px rgba(0,0,0,0.6) inset,
0 1px 2px rgba(0,0,0,0.5);
}
<ul class="piano">
<li data-tone="C1">C1</li>
<li data-tone="C1#" class="diesis">C1#</li>
<li data-tone="D1">D1</li>
<li data-tone="D1#" class="diesis">D1#</li>
<li data-tone="E1">E1</li>
<li data-tone="F1">F1</li>
<li data-tone="F1#" class="diesis">F1#</li>
<li data-tone="G1">G1</li>
<li data-tone="G1#" class="diesis">G1#</li>
<li data-tone="A1">A1</li>
<li data-tone="A1#" class="diesis">A1#</li>
<li data-tone="B1">B1</li>
</ul>
codepen 上的 full demo 也可以使用,如果它可以帮助调试。
谢谢
Chrome 中的音量问题可以通过仅使用一个全局 AudioContext
来解决,然后需要在点击处理程序中恢复。
可以通过向自动化时间线添加一个明确的值来消除 Firefox 中的噼啪声。
const currentTime = ac.currentTime;
gain.gain.setValueAtTime(1, currentTime);
gain.gain.linearRampToValueAtTime(.0001, currentTime + duration);
更新后的 JavaScript 您的 CodePen 代码将如下所示:
const ac = new AudioContext();
const noteFrequencies = {
'C1' : 261.63,
'C1#': 277.18,
'D1' : 293.66,
'D1#': 311.13,
'E1' : 329.63,
'F1' : 349.23,
'F1#': 369.99,
'G1' : 392.00,
'G1#': 415.30,
'A1' : 440.00,
'A1#': 466.16,
'B1' : 493.88,
'C2' : 523.25,
'C2#': 554.37,
'D2' : 587.33,
'D2#': 622.25,
'E2' : 659.25
}
function playTone(note, duration = 1) {
const currentTime = ac.currentTime;
let oscl = ac.createOscillator();
let gain = ac.createGain();
gain.connect(ac.destination);
oscl.connect(gain);
oscl.frequency.value = noteFrequencies[note];
oscl.type = "sine"
gain.gain.setValueAtTime(1, currentTime);
gain.gain.linearRampToValueAtTime(.0001, currentTime + duration);
oscl.start();
oscl.stop(currentTime + duration);
}
let notelist = document.querySelector('.piano');
notelist.addEventListener('click', (ev) => {
ac.resume();
let tgt = ev.target;
let tone;
if (tgt.matches('[data-tone]')) {
tone = tgt.getAttribute('data-tone');
playTone(tone);
}
});
我使用 AudioContext
对象做了一个纯 CSS/JS 钢琴键盘,但是我有两个与 playTone
函数相关的问题:
在Chrome/android (
v.89.0.4389.105/Android 10
) 似乎每按一个键音量就会减半:弹奏几个音符后音量就听不见了。在 Firefox (
v.88/MacOS 10.15.7
) 上,我听到每个按键结束时都发出噼啪声。
最新的Chrome/MacOS,为了比较,听起来不错。
const noteFrequencies = {
'C1' : 261.63,
'C1#': 277.18,
'D1' : 293.66,
'D1#': 311.13,
'E1' : 329.63,
'F1' : 349.23,
'F1#': 369.99,
'G1' : 392.00,
'G1#': 415.30,
'A1' : 440.00,
'A1#': 466.16,
'B1' : 493.88
}
function playTone(note, duration = 1) {
let ac = new AudioContext();
let oscl = ac.createOscillator();
let gain = ac.createGain();
gain.connect(ac.destination);
oscl.connect(gain);
oscl.frequency.value = noteFrequencies[note];
oscl.type = "sine"
gain.gain.linearRampToValueAtTime(.0001, ac.currentTime + duration);
oscl.start();
oscl.stop(ac.currentTime + duration);
}
let notelist = document.querySelector('.piano');
notelist.addEventListener('click', (ev) => {
let tgt = ev.target;
let tone;
if (tgt.matches('[data-tone]')) {
tone = tgt.getAttribute('data-tone');
playTone(tone);
}
});
* {
box-sizing: border-box;
font-family: "Lobster Two";
color: #555666;
}
h1 {
text-align: center;
font-size: 5rem;
margin-bottom: 4rem;
}
.piano {
position : relative;
display : flex;
width : max-content;
list-style : none;
margin : 0 auto;
padding : 0;
align-items: flex-start;
}
.piano li {
--ar : .2;
display: inherit;
cursor : pointer;
color : transparent;
user-select : none;
aspect-ratio: var(--ar);
}
@supports (not (aspect-ratio: 1)) {
.piano li::before {
content: "";
padding-top: calc(100% / var(--ar));
}
}
li:not(.diesis) {
width : max(50px, 4vw);
border-left : 1px solid #c4c4c8;
border-bottom : 1px solid #c4c4c8;
border-radius : 0 0 4px 4px;
background : linear-gradient(to bottom, #f2f2f5, #fff);
box-shadow : 0 0 5px #ccc inset;
}
li:not(.diesis):active {
border-top : 1px solid #bbb;
border-left : 1px solid #ccc;
border-bottom : 1px solid #ccc;
background : linear-gradient(to bottom, #fff, #f2f2f5);
box-shadow :
2px 0 3px rgba(0,0,0,0.1) inset,
-4px 2px 10px rgba(0,0,0,.02) inset;
}
li.diesis {
position : relative;
z-index : 1;
--w : max(30px, 2.5vw);
width : var(--w);
margin : 0 calc(var(--w) / -2);
border : 1px solid #131313;
border-radius : 0 0 4px 4px;
background : linear-gradient(40deg, #222, #555);
box-shadow :
0 -1px 1px 2px rgba(0,0,0, .5) inset,
0 1px 2px rgba(0,0,0, .5);
}
li.diesis:active {
background: linear-gradient(100deg, #505050, #131313);
box-shadow:
-1px -1px 2px rgba(255,255,255,0.2) inset,
0 -2px 2px 3px rgba(0,0,0,0.6) inset,
0 1px 2px rgba(0,0,0,0.5);
}
<ul class="piano">
<li data-tone="C1">C1</li>
<li data-tone="C1#" class="diesis">C1#</li>
<li data-tone="D1">D1</li>
<li data-tone="D1#" class="diesis">D1#</li>
<li data-tone="E1">E1</li>
<li data-tone="F1">F1</li>
<li data-tone="F1#" class="diesis">F1#</li>
<li data-tone="G1">G1</li>
<li data-tone="G1#" class="diesis">G1#</li>
<li data-tone="A1">A1</li>
<li data-tone="A1#" class="diesis">A1#</li>
<li data-tone="B1">B1</li>
</ul>
codepen 上的 full demo 也可以使用,如果它可以帮助调试。
谢谢
Chrome 中的音量问题可以通过仅使用一个全局 AudioContext
来解决,然后需要在点击处理程序中恢复。
可以通过向自动化时间线添加一个明确的值来消除 Firefox 中的噼啪声。
const currentTime = ac.currentTime;
gain.gain.setValueAtTime(1, currentTime);
gain.gain.linearRampToValueAtTime(.0001, currentTime + duration);
更新后的 JavaScript 您的 CodePen 代码将如下所示:
const ac = new AudioContext();
const noteFrequencies = {
'C1' : 261.63,
'C1#': 277.18,
'D1' : 293.66,
'D1#': 311.13,
'E1' : 329.63,
'F1' : 349.23,
'F1#': 369.99,
'G1' : 392.00,
'G1#': 415.30,
'A1' : 440.00,
'A1#': 466.16,
'B1' : 493.88,
'C2' : 523.25,
'C2#': 554.37,
'D2' : 587.33,
'D2#': 622.25,
'E2' : 659.25
}
function playTone(note, duration = 1) {
const currentTime = ac.currentTime;
let oscl = ac.createOscillator();
let gain = ac.createGain();
gain.connect(ac.destination);
oscl.connect(gain);
oscl.frequency.value = noteFrequencies[note];
oscl.type = "sine"
gain.gain.setValueAtTime(1, currentTime);
gain.gain.linearRampToValueAtTime(.0001, currentTime + duration);
oscl.start();
oscl.stop(currentTime + duration);
}
let notelist = document.querySelector('.piano');
notelist.addEventListener('click', (ev) => {
ac.resume();
let tgt = ev.target;
let tone;
if (tgt.matches('[data-tone]')) {
tone = tgt.getAttribute('data-tone');
playTone(tone);
}
});