使用WebAPI获取麦克风MediaStream(媒体流)+显示频谱+【控制麦克风音量】(.net FrameWork MVC)

使用WebAPI获取麦克风MediaStream(媒体流)+显示频谱+【控制麦克风输入音量】(.net FrameWork MVC)

一、使用WebAPI官网获取麦克风MediaStream(媒体流)+并实时显示频谱

 效果如图:

 传送门

选择媒体文件播放后(系统声音)显示其频谱可参考另一博客

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

 1/1、定义全局变量

var localStream;// 录音产生的音频流、麦克风收集到的音频数据
let audioCtx = null; // 音频上下文
let source = null; // 音频源 
let localStream = null; // 录音产生的音频流 录音产生的音频流、麦克风收集到的音频数据
let analyser = null; // 用于分析音频实时数据的节点(分析器) 

1/2利用Web Audio Api 处理获取麦克风音频流数据

获取到音频流之后,我们通过音频上下文AudioContext,创建音频源。这里选择MediaStreamAudioSourceNode,它接收一个MediaStream对象来创建音频源。 然后我们在音频源和destination中间插入一个音频节点,用来获取及处理音频数据,进而利用数据绘制出波形图。这里选择AnalyserNode,当然像ScriptProcessorNode和AudioWorkletNode节点也能够实现获取和处理实时音频数据,具体可以参考相关Api。
 当前业务-点击左上角呼叫台总开关-调用start方法(获取麦克风音频使用web audio api,我们在页面一加载就获取麦克风数据,否则出现时序问题
复制代码
$(function () {
    var constraints = {
        video: false,
        audio: true,//音频
    }
    if (!navigator.mediaDevices ||
        !navigator.mediaDevices.getUserMedia) {
        console.error('浏览器不支持getUserMedia')
        return;
    }
    else {
        navigator.mediaDevices.getUserMedia(constraints)
            .then(function gotMediaStream(stream) {      
                localStream = stream;//麦克风采集到的数据赋值给全局变量
                initAudioData(localStream);//调用音频数据处理方法--调用显示频谱的方法
            })
            .catch(handleError);
    }
}
复制代码

 1/3音频数据处理并绘制频谱   调用initAudioData方法  -参数为麦克风采集到的数据

复制代码
// 音频数据处理
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
initAudioData方法生成频谱 参数 stream为麦克风的媒体流
复制代码

 完成麦克风数据获取并显示频谱

采集成功后的麦克风MeidaStream(媒体流)样式

 

二、控制麦克风输入音量

官方网站:BaseAudioContext.createGain()

BaseAudioContext接口的createGain()方法创建了一个GainNode,它可以用来控制音频图的整体增益(或音量)。

1、设置全局变量extraGain

复制代码
// 16、定义全局变量localStream
let audioCtx = null; // 音频上下文
let source = null; // 音频源
let sysStream = null; // 系统声音音频流
let micStream = null;// 录音产生的音频流 录音产生的音频流、麦克风收集到的音频数据
let localStream = null;// 录音产生的音频流 录音产生的音频流、麦克风收集到的音频数据
let extraGain = null;  //麦克风控制
let analyser = null; // 用于分析音频实时数据的节点(分析器)

//定义全局变量pc1
var pc1;
复制代码

 

2、js编写控制音量方法  --参数为一个int数值

extraGain为全局变量--他的生成参考另一博客的第四点媒体流和麦克风混流方法中有生成extraGain(将其改为全局就可)
//麦克风音量控制  extraGain为全局变量  c的值为一个int值 1-100
function ctrmic(c) {
    if (extraGain != "") {   //为空表示呼叫台开关没开,extraGain这也没有生成
        extraGain.gain.setValueAtTime(c/100, audioCtx.currentTime);//目前理解设置他的音量值是0-1区间的数
    }
}

 

3、参考另一博客中绘制音量滑块代码--在滑动麦克风滑块的触发事件中调用上面控制音量的方法

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

如图:

posted @   じ逐梦  阅读(514)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· 单线程的Redis速度为什么快?
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
点击右上角即可分享
微信分享提示