惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

人人都是产品经理
人人都是产品经理
Blog — PlanetScale
Blog — PlanetScale
MyScale Blog
MyScale Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
WordPress大学
WordPress大学
Vercel News
Vercel News
D
Docker
博客园 - 聂微东
T
Tailwind CSS Blog
aimingoo的专栏
aimingoo的专栏
云风的 BLOG
云风的 BLOG
D
DataBreaches.Net
B
Blog RSS Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - Franky
Microsoft Security Blog
Microsoft Security Blog
美团技术团队
F
Fortinet All Blogs
MongoDB | Blog
MongoDB | Blog
T
The Blog of Author Tim Ferriss
GbyAI
GbyAI
N
Netflix TechBlog - Medium
G
Google Developers Blog
腾讯CDC

张鑫旭-鑫空间-鑫生活

1分钟速度了解text-align match-parent声明 « 张鑫旭-鑫空间-鑫生活 光标的形状也能设置了,就是CSS caret-shape属性 « 张鑫旭-鑫空间-鑫生活 pretext与文字四面环绕效果的实现 « 张鑫旭-鑫空间-鑫生活 JSON.rawJSON方法的作用是什么? « 张鑫旭-鑫空间-鑫生活 如何使用CSS判断鼠标从哪个方向进入元素? « 张鑫旭-鑫空间-鑫生活 借助mediabunny纯JS实现视频水印、剪裁、合成等功能 « 张鑫旭-鑫空间-鑫生活 务必谨慎使用JS WeakRef弱引用 « 张鑫旭-鑫空间-鑫生活 CSS六边形头像的实现与蜂巢布局 « 张鑫旭-鑫空间-鑫生活 CSS corner-shape与背景底纹技术 « 张鑫旭-鑫空间-鑫生活 浅学WebTransport API:下一代Web双向通信技术 « 张鑫旭-鑫空间-鑫生活 HTML dialog元素新支持了closedBy属性 « 张鑫旭-鑫空间-鑫生活 HTML interestfor属性与悬停popover交互效果 « 张鑫旭-鑫空间-鑫生活 CSS3模拟window7炫酷界面效果展示 « 张鑫旭-鑫空间-鑫生活 图片旋转+剪裁js插件(兼容各浏览器) « 张鑫旭-鑫空间-鑫生活 纯js无图片无外链CSS图片幻灯片播放插件 « 张鑫旭-鑫空间-鑫生活 CSS border三角、圆角图形生成技术简介 « 张鑫旭-鑫空间-鑫生活 CSS “渐进增强”在web制作中常见应用举例 « 张鑫旭-鑫空间-鑫生活 让IE6/IE7/IE8浏览器支持CSS3属性 « 张鑫旭-鑫空间-鑫生活 jQuery – 高亮动态显示页面HTML代码插件 « 张鑫旭-鑫空间-鑫生活 翻译 – 逐渐消失的Flash网站 « 张鑫旭-鑫空间-鑫生活 CSS reset的重新审视 – 避免样式重置 « 张鑫旭-鑫空间-鑫生活 新浪微博插入话题后部分文字选中的js实现 « 张鑫旭-鑫空间-鑫生活 JavaScript实现http地址自动检测并添加URL链接 « 张鑫旭-鑫空间-鑫生活 CSS实现兼容性的渐变背景(gradient)效果 « 张鑫旭-鑫空间-鑫生活 CSS content内容生成技术以及应用 « 张鑫旭-鑫空间-鑫生活 CSS gradient渐变之webkit核心浏览器下的使用 « 张鑫旭-鑫空间-鑫生活 CSS渐变之CSS3 gradient在Firefox3.6下的使用 « 张鑫旭-鑫空间-鑫生活 区分IE8/IE7/IE6及其他浏览器-CSS “\9″hack « 张鑫旭-鑫空间-鑫生活 CSS实现跨浏览器兼容性的盒阴影效果 « 张鑫旭-鑫空间-鑫生活 CSS3&HTML5各浏览器支持情况一览表 « 张鑫旭-鑫空间-鑫生活
独家,MP3音频淡入淡出播放和转换的JS实现 « 张鑫旭-鑫空间-...
张鑫旭,zhangxinxu · 2024-07-29 · via 张鑫旭-鑫空间-鑫生活

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11293
本文可全文转载,独立域名个人网站无需授权,但需要保留原作者、出处以及文中链接,任何网站均可摘要聚合,商用请联系授权。

封面占位图

一、需求背景

希望特效音可以有淡入淡出效果,同时能够在视频合成的时候体现在音轨中。

二、播放淡入淡出

先讲下偏浅层应用的技术实现,那就是播放淡入淡出。

1. 使用howlerjs实现

howlerjs这个项目(https://github.com/goldfire/howler.js)我多次提到过,凡是音频播放的需求(除了那种简单的点击播放),无需任何迟疑,也不用考虑其他,就是要howlerjs。

两三万Star的项目,品质保证。

下面演示下如何实现。

假设页面上有个按钮,点击按钮播放某音频,HTML示意:

<button id="button">点击播放</button>

则下面几行JavaScript代码可以实现音频播放淡入,结束淡出的效果。

const url = './htmlbook.wav';

const sound = new Howl({
    src: [url]
});

// 点击按钮,音频淡入淡出播放
button.onclick = function () {
    sound.play();
    sound.fade(0, 1, 1000);
    setTimeout(() => {
        sound.fade(1, 0, 1000);
    }, sound.duration() * 1000 - 1500);
}

根据我查找的资料,howlerjs并未提供内置的结尾淡出方法,只有一个fade()方法,因此,结束时候声音淡出使用了定时器。而之所以定时器offset的时间是1500ms而不是淡出的1000ms时间,是因为案例使用的htmlbook.wav末尾有一段声音是静音,为了让淡出效果明显,才如此处理的,不代表通用场景。

眼见为实,您可以狠狠地点击这里:使用Howler.js实现音频播放淡入淡出demo

点击下图所示的按钮,就可以听到WAV音频声音fade播放的效果了。

音频播放demo示意

2. 使用原生的GainNode实现

JS Web Audio API提供了原生的音量动态函数变化方法。包括:

下面,我们使用GainNode对象和linearRampToValueAtTime()方法示意如何实现音频播放首尾淡入淡出效果,和上面按钮同样的HTML代码,就一个按钮,然后JS代码如下所示。

const url = './htmlbook.wav';

// 点击按钮,音频淡入淡出播放
button.onclick = function () {
    // 请求数据
    fetch(url).then(res => res.arrayBuffer()).then(buffer => {
        const audioContext = new AudioContext();
        audioContext.decodeAudioData(buffer, function (audioBuffer) {
            const source = audioContext.createBufferSource();
            const gainNode = audioContext.createGain();
            // 起始音量静音
            gainNode.gain.value = 0;
            gainNode.connect(audioContext.destination);
            // buffer数据设置
            source.buffer = audioBuffer;
            source.connect(gainNode);
            source.start();

            gainNode.gain.linearRampToValueAtTime(1.0, audioContext.currentTime + 1);

            // 时长计算
            const duration = audioBuffer.duration;

            setTimeout(() => {
                gainNode.gain.linearRampToValueAtTime(0.01, audioContext.currentTime + 1);
            }, duration * 1000 - 1200);
        });
    });
}

此时,点击按钮,就可以听到声音淡入淡出播放的效果了。例如,点击下面这个按钮:

如果没有效果,多半是在第三方网站,狠击这里访问实例:使用GainNode实现音频淡入淡出播放demo

补充说明

根据自己和官方demo测试,exponentialRampToValueAtTime()函数执行的时候,声音是突然变化的,不知道是不是我设备的问题。

三、音频底层淡入淡出转换

如果我们希望对音频进行转换,也就是直接把原始音频转换成淡入淡出的音频,就需要处理音频的AudioBuffer数据。

这个就相对深入些了,原理什么的大家应该都不关心,不啰嗦,3,2,1,直接上代码,见:

const sliceAudio = function (buffer, start, end, fadeIn = 0, fadeOut = 0) {
  const sampleRate = buffer.sampleRate;
  const audioContext = new AudioContext();

  const length = Math.round((end - start) * sampleRate);
  const offset = Math.round(start * sampleRate);
  const newBuffer = audioContext.createBuffer(buffer.numberOfChannels, length, sampleRate);

  for (let channel = 0; channel < buffer.numberOfChannels; channel++) {
    const inputData = buffer.getChannelData(channel);
    const outputData = newBuffer.getChannelData(channel);

    for (let i = 0; i < length; i++) {
      outputData[i] = inputData[offset + i];

      // Apply fade in
      if (i < fadeIn * sampleRate) {
        outputData[i] *= i / (fadeIn * sampleRate);
      }

      // Apply fade out
      if (i > length - fadeOut * sampleRate) {
        outputData[i] *= (length - i) / (fadeOut * sampleRate);
      }
    }
  }

  return newBuffer;
}

其中:

buffer
需要转换的AudioBuffer数据
start
音频复制起始时间
end
音频复制结束时间,如果结束时间就是音频时长,此参数可以设置为buffer.duration
fadeIn
开头淡入的时长,单位是秒
fadeOut
结束淡出的时长,单位是秒

具体如何应用呢?可以参见下面的演示页面。

您可以狠狠地点击这里:MP3/Wav AudioBuffer转换成淡入淡出音频demo

点击“转换”按钮,下面的音频播放器播放的就是被淡入淡出处理后的音频,点击现在我们可以得到这个新的WAV音频文件。

淡入淡出转换截图示意

主要应用代码示意(从点击按钮开始):

const url = './htmlbook.wav';

// 点击按钮,音频淡入淡出播放
button.onclick = function () {
  // 请求数据
  fetch(url).then(res => res.arrayBuffer()).then(buffer => {
    const audioContext = new AudioContext();
    audioContext.decodeAudioData(buffer, (audioBuffer) => {
      const convertBuffer = sliceAudio(audioBuffer, 0, audioBuffer.duration, 1, 1.5);
      // 转blob WAV
      const wavBuffer = audioBufferToWav(convertBuffer);
      const wavBlob = new Blob([wavBuffer], { type: 'audio/wav' });
      const urlBlob = URL.createObjectURL(wavBlob);

      // create download link and append to Dom
      const downloadLink = document.createElement('a');
      downloadLink.href = urlBlob;
      downloadLink.setAttribute('download', 'htmlbook-fade.wav');
      downloadLink.textContent = '下载';

      // 按钮禁用
      this.disabled = true;
      // 试听支持
      audio.src = urlBlob;
      // 下载支持
      audio.after(downloadLink);
    });
  });
}

更完整的处理代码,参见上面的演示页面的源代码(演示页面省略了AudioBuffer转Wav blob数据的代码)。

四、来了来了,本周的碎碎念

周三的时候,我的新书已经是京东日榜第1了,看来口碑发酵了,感谢大家的正版支持。

日榜第1

CSS世界精讲视频继续更新,关于新书介绍的文字版,在自己博客和公众号也都发布了。

视频下面都是有抽奖活动的。

目前累计送出去14本《HTML并不简单》,3本《CSS新世界》,还包了400~500的红包,前期活动成本都1000块钱出去了。

关键还要看后期如何了,只是国内盗版盛行,好忧虑啊。

另外,不少人提供了宝贵的勘误,特别感谢,重印的时候会调整。

上周是平凡的一周,小朋友去了浦东图书馆,游了泳,家里领导周六还听了莫文蔚的演唱会,我呢继续抽空钓鱼。

钓的都是廉价鱼塘,80元还有88元的,果然难钓的一塌糊涂,前者就4条鲤鱼,后者4条半斤鲫鱼和两条太阳鱼。

没办法,作为探钓博主,不能挑地方,好与不好都要去。

下半周天很热,是不是可以找个凉快的地方避暑呢?

?

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=11293

(本篇完)