js实现麦克风+系统扬声器语音录制和播放

今天写项目时,有一个需求:通过前端实现用户语音、系统扬声器语音的输入与输出,第一个还是比较好实现的,问问GPT就会给你生成代码,问题的难点就在于第2条:如何获取电脑扬声器的输出,问gpt,gpt说不可以实现,在经过查阅大量资料和不断的尝试后,我终于找到了问题的解决办法:通过访问屏幕媒体流来获取数据,间接的拿到音频数据!

js已经内置好了方法,直接调用即可,不需要下载什么插件啥的,下面分别解释一下获取麦克风的权限,声音录制与播放,还有系统扬声器声音的录制与播放:

1-麦克风权限:

const stream = await navigator.mediaDevices.getUserMedia({ audio: true });

让我们逐步解析这行代码的各个部分:

  1. navigator.mediaDevices:
    • navigator 是Web API中的一个全局对象,它提供了关于浏览器的信息,并允许脚本执行一些浏览器级别的操作,比如页面跳转或查询浏览器设置。
    • mediaDevicesnavigator对象的一个属性,它提供了一个接口,允许用户访问连接到设备的媒体输入和输出设备,如摄像头和麦克风。
  2. getUserMedia() 方法:
    • getUserMedia()mediaDevices接口中的一个方法,用于请求用户媒体的访问权限。这个方法返回一个Promise,这是因为请求用户媒体是一个异步操作,需要等待用户授权或拒绝。
    • 这个方法接受一个配置对象作为参数,用于指定需要访问的媒体类型。在你的例子中,配置对象是{ audio: true },表示请求访问用户的音频设备(通常是麦克风)。
  3. 配置对象 { audio: true }:
    • 这个对象指定了getUserMedia()方法的参数,即请求访问用户的音频输入设备。如果还需要访问视频输入设备(如摄像头),可以添加video: true或者指定更详细的视频约束(如分辨率、帧率等)。
  4. await 关键字 :
    • 由于getUserMedia()方法返回一个Promise,你可以使用await关键字等待这个Promise解析完成。await只能在异步函数(即声明为async的函数)内部使用。
    • 使用await可以让代码看起来像是同步执行的,即等待getUserMedia()Promise解析后再继续执行后续代码。如果Promise被拒绝(例如,用户拒绝访问请求),则await表达式会抛出一个异常,这通常需要被try...catch语句捕获。
  5. const stream = ... :
    • 如果getUserMedia()成功,它会解析为一个MediaStream对象,这个对象代表了用户媒体设备的实时音频/视频流。
    • 在这个例子中,这个MediaStream对象被赋值给了一个名为stream的常量。你可以使用这个stream对象来播放音频或视频,或者将其发送到服务器进行进一步处理。

综上所述,这行代码的作用是异步请求访问用户的音频设备(麦克风),并将获取到的音频流存储在stream变量中。这是实现Web应用中音频录制功能的基础步骤之一。

2-系统扬声器权限

const stream = await navigator.mediaDevices.getDisplayMedia({audio: true});

可以看到,和上面方法做对比仅仅是把getUserMedia****换成了 getDisplayMedia。

后边的这段代码本来的功能其实是为了获取屏幕数据的,但是再参数里设置的audio:ture就可以神奇的获取到电脑音频数据了,随后再提取音频流进行保存:

screenMediaRecorder = new MediaRecorder(stream);

screenMediaRecorder.ondataavailable = event => {

audioChunks.push(event.data);

};

就成功的拿到了音频数据了。

3 -获取电脑音频操作步骤:

第一步:运行代码,然后浏览器就会弹出这个窗口

第二步:选择"整个屏幕"这样的话就可以获得整个屏幕的声音输出

第三步:再次点击窗口里的整个屏幕

第三步:点击同时分享系统音频

第四步:点击分享

4-源码分享

源码放在这了,有需要的朋友自取,记得点赞收藏支持博主

html 复制代码
<!DOCTYPE html>  
<html lang="en">  
<head>  
<meta charset="UTF-8">  
<meta name="viewport" content="width=device-width, initial-scale=1.0">  
<title>语音录制与播放</title>  
<style>  
  .button-row {  
    display: flex;  
    justify-content: space-around;  
    margin-bottom: 10px;  
  }  
  button {  
    padding: 10px 20px;  
    font-size: 16px;  
    margin: 5px;  
  }  
</style>  
</head>  
<body>  
  
<div class="button-row">  
  <button id="startRecord">1-开始录制语音</button>  
  <button id="stopRecord">2-停止录制语音</button>  
  <button id="playRecord">3-播放录制的声音</button>  
</div>  
  
<div class="button-row">  
  <button id="startScreenRecord">4-获取系统扬声器声音</button>  
  <button id="stopScreenRecord">5-停止获取系统扬声器声音</button>  
  <button id="playScreenRecord">6-播放系统扬声器声音</button>  
</div>  
  
<audio id="audioPlayback" controls style="display:none;"></audio>  
<audio id="audioPlayback2" controls style="display:none;"></audio>
<script>  
let mediaRecorder;  
let screenMediaRecorder;  
let audioChunks = [];  
  
document.getElementById('startRecord').addEventListener('click', async () => {  
  try {  
    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });  
    mediaRecorder = new MediaRecorder(stream);  
    mediaRecorder.ondataavailable = event => {  
      audioChunks.push(event.data);  
    };  
    mediaRecorder.onstop = () => {  
      const audioBlob = new Blob(audioChunks, { 'type': 'audio/wav' });  
      const audioUrl = URL.createObjectURL(audioBlob);  
      document.getElementById('audioPlayback').src = audioUrl;  
      document.getElementById('audioPlayback').style.display = 'block';  
      audioChunks = []; // Reset for next recording  
      mediaRecorder = null;
      stream.getTracks().forEach(track => {  
        track.stop();  
    });  
    };  
    mediaRecorder.start();  
  } catch (err) {  
    console.error("获取用户语音失败: ", err);  
  }  
});  
  
document.getElementById('stopRecord').addEventListener('click', () => {  
  if (mediaRecorder) {  
    mediaRecorder.stop();  
  }  
});  
  
document.getElementById('playRecord').addEventListener('click', () => {  
  const audioPlayback = document.getElementById('audioPlayback');  
  if (audioPlayback.paused) {  
    audioPlayback.play();  
  } else {  
    audioPlayback.pause();  
  }  
});  


document.getElementById('startScreenRecord').addEventListener('click', async()=> {
    try {  
    // 请求访问用户的屏幕媒体流  
    const stream = await navigator.mediaDevices.getDisplayMedia({audio: true});  
    screenMediaRecorder = new MediaRecorder(stream);  
    screenMediaRecorder.ondataavailable = event => {  
      audioChunks.push(event.data);  
    };  
    screenMediaRecorder.onstop = () => {  
      const audioBlob = new Blob(audioChunks, { 'type': 'audio/wav' });  
      const audioUrl = URL.createObjectURL(audioBlob);  
      document.getElementById('audioPlayback2').src = audioUrl;  
      document.getElementById('audioPlayback2').style.display = 'block';  
      audioChunks = []; // Reset for next recording  
      screenMediaRecorder = null;
      stream.getTracks().forEach(track => {  
        track.stop();  
    });  
    };  
    screenMediaRecorder.start();    
  } catch (err) {  
    console.error("获取用户语音失败: ", err);  
  }  
});
document.getElementById('stopScreenRecord').addEventListener('click', () => {  
  if (screenMediaRecorder) {  
    screenMediaRecorder.stop();  
  }  
}); 
document.getElementById('playScreenRecord').addEventListener('click', () => {  
  const audioPlayback = document.getElementById('audioPlayback2');  
  if (audioPlayback.paused) {  
    audioPlayback.play();  
  } else {  
    audioPlayback.pause();  
  }  
}); 

</script>  
  
</body>  
</html>
相关推荐
YongGit9 分钟前
探索 AI + MCP 渲染前端 UI
前端·后端·node.js
慧一居士1 小时前
<script setup>中的setup作用以及和不带的区别对比
前端
RainbowSea1 小时前
NVM 切换 Node 版本工具的超详细安装说明
java·前端
读书点滴1 小时前
笨方法学python -练习14
java·前端·python
Mintopia1 小时前
四叉树:二维空间的 “智能分区管理员”
前端·javascript·计算机图形学
慌糖1 小时前
RabbitMQ:消息队列的轻量级王者
开发语言·javascript·ecmascript
Mintopia2 小时前
Three.js 深度冲突:当像素在 Z 轴上玩起 "挤地铁" 游戏
前端·javascript·three.js
Penk是个码农2 小时前
web前端面试-- MVC、MVP、MVVM 架构模式对比
前端·面试·mvc
MrSkye2 小时前
🔥JavaScript 入门必知:代码如何运行、变量提升与 let/const🔥
前端·javascript·面试
白瓷梅子汤2 小时前
跟着官方示例学习 @tanStack-form --- Linked Fields
前端·react.js