使用WebAPI获取【系统媒体】(页面背景音乐)MediaStream(媒体流)+显示频谱+【控制音量/快进】(.net FrameWork MVC)

 使用WebAPI获取系统媒体(页面背景音乐)MediaStream(媒体流)+显示频谱+控制音量

WebAPI接口参考

1、页面布局添加audio标签

//controls  控制界面中是否显示audio控件
<audio id="playaudio1" src="" controls></audio>@*系统音乐*@

   

2、自定义设计界面--通过自定义点击界面音乐实现类似于音乐播放器的功能(图示说明)

可参考另一博客:呼叫台功能页面布局【canvas绘制音频频谱图】【绘制音量控制滑块】 (示例使用.net framework mvc) - じ逐梦 - 博客园 (cnblogs.com)

播放效果如图:

    

3、成功播放后js编写代码获取媒体的MediaStream(媒体流)

Web Audio API 的运用 - Web API 接口参考 | MDN (mozilla.org)

AudioContext.createMediaElementSource() - Web API 接口参考 | MDN (mozilla.org)

AudioContext 接口的 createMediaElementSource() 方法用于创建一个新的 MediaElementAudioSourceNode 对象,输入某个存在的 HTML <audio> or <video> 元素,对应的音频即可被播放或者修改。

由 <audio> 元素,通过使用 createMediaElementSource() 方法,创建一个音源

3-1、通过AudioContext上下文获取()创建上下文  (有了上下文可以干好多事)

const AudioContext = window.AudioContext || window.webkitAudioContext;
const audioContext = new AudioContext();

利用HTML5 Web Audio API给网页JS交互增加声音 « 张鑫旭-鑫空间-鑫生活 (zhangxinxu.com)

3-2、获取auido元素   //通过auido标签获取该媒体音源--创建一个接口用来关联HTMLMediaElement 这可以用来播放和处理来自video或audio元素的音频

(注意track和stream_dest要声明为全局变量否则会有最下方解决问题的第一个问题出现)

AudioContext 接口的 createMediaElementSource() 方法用于创建一个新的 MediaElementAudioSourceNode 对象,输入某个存在的 HTML <audio> or <video> 元素,对应的音频即可被播放或者修改。

 //获取audio标签
 var audioElement = document.querySelector('audio#playaudio');
 //通过auido标签获取该媒体音源--创建一个接口用来关联HTMLMediaElement 这可以用来播放和处理来自video或audio元素的音频
 const track = audioContext.createMediaElementSource(audioElement);

3-3、获取本地媒体的MediaStream

(注意track和stream_dest要声明为全局变量否则会有最下方解决问题的第一个问题出现)

AudioContext.createMediaStreamDestination() - Web API 接口参考 | MDN (mozilla.org)

AudioContext接口的 createMediaStreamDestination() 方法用于创建一个新的对象,该对象关联着表示音频流的一个 WebRTC (en-US) MediaStream,音频流可以存储在本地文件或者被发送到另外一台计算机。

AudioContext的 destination 属性返回一个 AudioDestinationNode,表示 context 中所有音频的最终目标节点,一般是音频渲染设备,比如扬声器。

   //获取本地audio的MediaStream
   var stream_dest = audioContext.createMediaStreamDestination();

3-4、建立连接(暂时没搞懂原因)  需要将我们的音频图从音频源/输入节点连接到目的地

 track.connect(stream_dest);

3-5、成功获取MedisStream  --stream_dest即为所需

stream_dest数据如图:

 提取stream_dest中的stream即为媒体的MedisStream   数据如图:stream_dest.stream

4、本地测试获取到的MediaStream

页面可添加一个测试audio标签用来表示输出,将获取到的MediaStream通过srcObject属性测试

//获取测试audio
var playaudio1 = document.querySelector('audio#playaudio1');//设置赋值
playaudio1.srcObject = stream_dest.stream;

 

成功效果:(左边为输入-右边为输出(MediaStream数据正确通过赋值后会有其效果))

5、获取到了MediaStream就可传进WebRTC

实现呼叫台博客的功能(调第三方的api的推流技术,通过切换音乐实现音箱播放相应音乐)

 调用第三方接口实现呼叫台功能+麦克风数据数据并展示频谱 -WebRTC - じ逐梦 - 博客园 (cnblogs.com)

6、以上完整代码:

复制代码
functin  getSysMediaStream{
        //创建AudioContext上下文
        const AudioContext = window.AudioContext || window.webkitAudioContext;
        const audioContext = new AudioContext();
        //获取audio标签
        var audioElement = document.querySelector('audio#playaudio');
     
//标黄这两步需要做一个逻辑判断处理-最下方解决问题的第一个有详细说明
     //
通过auido标签获取该媒体音源--创建一个接口用来关联HTMLMediaElement 这可以用来播放和处理来自video或audio元素的音频 const track = audioContext.createMediaElementSource(audioElement); //获取本地audio的MediaStream音频 var stream_dest =
audioContext.createMediaStreamDestination(); track.connect(stream_dest); //获取测试audio var playaudio1 = document.querySelector('audio#playaudio1'); //设置赋值 playaudio1.srcObject = stream_dest.stream;
        //显示频谱
        initAudioData(stream_dest.stream)
}
复制代码

二、显示频谱:

 将生成的MediaStream放入生成频谱方法中实现显示频谱(频谱详细显示可参考另一博客麦克风采集信息显示频谱方式)

调用第三方接口实现呼叫台功能+麦克风数据数据并展示频谱 -WebRTC - じ逐梦 - 博客园 (cnblogs.com)

复制代码
// 音频数据处理
function initAudioData(stream) {
    console.error("1");
    console.error(stream);
    //localStream = stream;
    // 创建音频上下文
    audioCtx = new (window.AudioContext || window.webkitAudioContext)();
    // 创建音频源   -将声音输入这个对象
    source = audioCtx.createMediaStreamSource(stream);
    // 创建音频分析节点
    analyser = audioCtx.createAnalyser();
    // fftSize决定了能够获取到的音频数据的数量
    analyser.fftSize = 4096;
    // 音频源连接至analyser
    source.connect(analyser);
    // analyserNode再连接至扬声器播放--本人不需要暂时注释掉了
    analyser.connect(audioCtx.destination);
    // 简单用定时器来绘制波形图,该文档使用requestAnimationFrame来以屏幕刷新的评率来反复执行绘制函数
    //animateFrame = setInterval(drawWaver, 60);

    //开始绘制频谱图
    var canvas = document.getElementById('canvas'),
        cwidth = canvas.width,
        cheight = canvas.height - 2,
        meterWidth = 10,//能量条的宽度
        gap = 2,//能量条的间距
        meterNum = 800 / (10 + 2);//计算当前画布上能画多少条
    var ctx = canvas.getContext('2d');
    var capHeight = 2,//冒头的高度
        capStyle = '#fff',//冒头的颜色
        capYPositionArray = [];//将上一面各个冒头的位置保存到这个数组1e9fff
    //定义一个渐变样式用于画图
    var gradient = ctx.createLinearGradient(0, 0, 0, 300);
    //gradient.addColorStop(1, '#0f0');
    //gradient.addColorStop(0.5, '#ff0');
    //gradient.addColorStop(0, '#f00');
    gradient.addColorStop(1, '#1e9fff');//低音颜色
    gradient.addColorStop(0.5, '#70bbf2');//中音颜色
    gradient.addColorStop(0, '#1e9fff');//高音颜色
    //绘制频谱图
    function drawSpectrum() {
        var array = new Uint8Array(analyser.frequencyBinCount);
        analyser.getByteFrequencyData(array);
        var step = Math.round(array.length / meterNum);//计算从analyser中的采样步长
        //清理画布
        ctx.clearRect(0, 0, cwidth, cheight);
        //对信源数组进行抽样遍历,画出每个频谱条
        for (var i = 0; i < meterNum; i++) {
            var value = array[i * step]; //取样作为y轴的值
            //绘制缓慢降落的冒头
            if (capYPositionArray.length < Math.round(meterNum)) {
                capYPositionArray.push(value);//初始化保存冒头位置的数组,将第一个画面位置保存
            }
            ctx.fillStyle = capStyle;
            //1.开始绘制冒头
            if (value < capYPositionArray[i]) {
                //使用前一次数据
                ctx.fillRect(i * 12, cheight - (--capYPositionArray[i]), meterWidth, capHeight);
            } else {
                //否则,直接使用当前数据并记录
                ctx.fillRect(i * 12, cheight - value, meterWidth, capHeight);
                capYPositionArray[i] = value;
            }
            //2.开始绘制频谱条
            ctx.fillStyle = gradient;
            ctx.fillRect(i * 12/*频谱条的宽度+条间距*/, cheight - value + capHeight,
                meterWidth, cheight);
        }
        //用requestAnimationFrame来以屏幕刷新的评率来反复执行绘制函数
        requestAnimationFrame(drawSpectrum);
    }
    //用requestAnimationFrame来以屏幕刷新的评率来反复执行绘制函数
    requestAnimationFrame(drawSpectrum);
}
initAudioData
复制代码

三.控制音量

就是控制audio的音量 

参考另一博客中绘制音量滑块代码--在滑动系统声音滑块的触发事件中调用下面控制音量的方法

呼叫台功能页面布局【canvas绘制音频频谱图】【绘制音量控制滑块】 (示例使用.net framework mvc) - じ逐梦 - 博客园 (cnblogs.com)

(6条消息) 解决audio控制播放音量_weixin_33831196的博客-CSDN博客

//系统音乐音量控制  参数是int值  1-100    
function ctrsys(s) {
    //根据audio的id获取元素
    const playaudio = document.querySelector('audio#playaudio');
    //设置音量  0-1  1表示音量最大
    playaudio.volume = s/100;
}

 9、根据自己编辑滑块滑动控制进度

参考另一博客:在播放进度条滑块滑动事件和进度条点击事件中调用该方法

(6条消息) js-audio对象-音频倍速播放、定位播放_sunxiaolinlinx的博客-CSDN博客

//系统音乐快进    参数是int值   单位:秒
function sspeed(p) {
    const playaudio = document.querySelector('audio#playaudio');
    playaudio.currentTime = p;
}

 

10、音乐播放结束根据当前模式(单曲循环、顺序播放、随机播放)继续播放

(6条消息) video 标签 监听播放、暂停、结束等事件_爱吃烧鸭蛋的叶安的博客-CSDN博客_video标签 捕捉播放结束

 

问题解决:

1、通过采用先获取audio元素,然后获取其媒体信息(获取MediaStream),当媒体暂停重新播放出现错误

 问题解决:

经翻译可以看出HTMLMediaElement之前已经连接到一个不同的MediaElementSourceNode。

所以修改之前代码,需要将上面的(trackstream_dest要声明为全局变量并且加上逻辑判断,页面没有刷新情况下只会在第一次给其赋值

修改代码为:

两个变量设为全局:

 //元素音源
let track = null;  
//创建一个新的对象,该对象关联着表示音频流的一个 WebRTC (en-US) MediaStream
let stream_dest = null;

获取元素的MediaStream方法添加逻辑判断--添加作用原因就是页面在不刷新情况下不会重复生成新的【关联音频流stream_dest的对象】和【音源track】

此处的audioCtx就是上面详细代码中的audioContext上下文

复制代码
//获取系统媒体stream
function getSysStream() {
    //获取音源 将音源放入audioContext中
    // 获取界面auido标签 
    const audioElement = document.querySelector('audio');
    if (track == null) {
        //通过使用 createMediaElementSource() 方法,创建了一个音源
        track = audioCtx.createMediaElementSource(audioElement);
        stream_dest = audioCtx.createMediaStreamDestination();
    }
    track.connect(stream_dest);
    sysStream = stream_dest.stream;//获取系统声音stream
}
getSysStream()
复制代码

 上面控制台提示的错误实现解决

posted @   じ逐梦  阅读(457)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示