微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

音频上下文演示 Chrome/Android 上的音量减半,Firefox 上爆裂

如何解决音频上下文演示 Chrome/Android 上的音量减半,Firefox 上爆裂

我使用 AudioContext 对象做了一个纯 CSS/JS 钢琴键盘,但我有两个与 playTone 函数相关的问题:

  1. 在 Chrome/android (v.89.0.4389.105/Android 10) 上,似乎每次按下键时音量都会减半:弹奏几个音符后,音量就听不见了。

  2. 在 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.1) inset,-4px 2px 10px rgba(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,.5) inset,0 1px 2px rgba(0,.5);
   
}

li.diesis:active {
  background: linear-gradient(100deg,#505050,#131313);
  Box-shadow: 
     -1px -1px 2px rgba(255,255,0.2) inset,0 -2px 2px 3px rgba(0,0.6) inset,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);

您的 CodePen 更新后的 JavaScript 代码将如下所示:

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);   
  }
});

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。