HTML5 getUserMedia/AudioContext 打造音谱图形化

摘要:
需求是分析音频并以图形方式显示。思考:1.回想当年使用的播放器,例如XX听音,通常显示两种图形,一种是条形柱,另一种是波纹。2.分析数据被转换成图像,这是canvas常用的。之前做过的画布是分析图像数据、制作过滤器和变形的好手。当然,图形也提供给画布。3.由于要分析音频,因此有必要将音频转换为数据进行分析。用于音频的HTML API具有音频标签,麦克风访问具有getUserMedia。我怎么知道这个API?

需求是分析音频,用图形化展示。

HTML5 getUserMedia/AudioContext 打造音谱图形化第1张

HTML5 getUserMedia/AudioContext 打造音谱图形化第2张

HTML5 getUserMedia/AudioContext 打造音谱图形化第3张

思路:

  1、回想当年使用的播放器,如XX静听 一般就2种图形化展示 一个是条形柱 一个是波纹

  2、分析数据转化成图像 这个是canvas常用的,之前做过的canvas分析图像数据,做滤镜做变形都是一把好手,这里当然 图形化也交给canvas了。

  3、既然是分析音频,那当然要将音频转化成数据,才可以进行分析,而关于音频的HTML API 就有 audio标签 ,而麦克风访问就有getUserMedia了。什么?你问我咋知道这个api的?我只能告诉你 去查MDN 、W3C 这类网站...

首先我们要得到音频数据,这里我们用了2个途径得到,一个是音频流,一个是麦克风

1、api兼容

window.AudioContext = (window.AudioContext || window.webkitAudioContext || window.mozAudioContext);
window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame;
try {
    audioCtx = new AudioContext;
    console.log('浏览器支持AudioContext');
} catch(e) {
    console.log('浏览器不支持AudioContext', e);
};

2、得到麦克风数据

//开始监听            
if (navigator.getUserMedia) {
    console.log('浏览器支持getUserMedia');
    apiMedia.className = "checked";
    navigator.getUserMedia(
    // 我们只获取麦克风数据,这里还可以设置video为true来获取摄像头数据
    {
        audio: true
    },
    onSccess, onErr)
} else {
    console.log('浏览器不支持getUserMedia');
    apiMedia.className = "false";
};

通过onSccess来得到数据,介入分析

// Success callback
function onSccess(stream) {
    //将声音输入对像
    source = audioCtx.createMediaStreamSource(stream);
    source.connect(analyser);
    analyser.connect(distortion);
    distortion.connect(biquadFilter);
    biquadFilter.connect(convolver);
    convolver.connect(gainNode);
    gainNode.connect(audioCtx.destination);

    visualize();//分析音频
}

同样,文件的也是一样的

if (mediaSetting == "file") {
    loadFile();
    //得到数据源
    source=audioCtx.createMediaElementSource(audio);
    //连接节点
    source.connect(analyser);
    analyser.connect(distortion);
    distortion.connect(gainNode);
    gainNode.connect(audioCtx.destination);
}
visualize();//分析音频

得到了数据,接下来就交个canvas君吧

//波纹1
if (visualSetting == "wave1") {
    analyser.fftSize = 2048;
    var bufferLength = analyser.fftSize;
    console.log(bufferLength);
    var dataArray = new Uint8Array(bufferLength);
    canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);

    draw = function() {
        drawVisual = requestAnimationFrame(draw);
        analyser.getByteTimeDomainData(dataArray);

        canvasCtx.fillStyle = '#000';
        canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
        canvasCtx.lineWidth = 2;
        canvasCtx.strokeStyle = '#4aeb46';
        canvasCtx.beginPath();

        var sliceWidth = WIDTH * 1.0 / bufferLength;
        var x = 0;

        for (var i = 0; i < bufferLength; i++) {
            var v = dataArray[i] / 128.0;
            var y = v * HEIGHT / 2;
            if (i === 0) {
                canvasCtx.moveTo(x, y);
            } else {
                canvasCtx.lineTo(x, y);
            }
            x += sliceWidth;
        }
        canvasCtx.lineTo(canvas.width, canvas.height / 2);
        canvasCtx.stroke();
    };
    draw();
}
//circle
else if (visualSetting == "circle") {
    // analyser.fftSize = 1024;
    // var bufferLength = analyser.fftSize;
    // var dataArray = new Uint8Array(bufferLength);
    analyser.fftSize = 128;
    var frequencyData = new Uint8Array(analyser.frequencyBinCount);
    var count = analyser.frequencyBinCount;
    var circles = [];
    var circleMaxWidth = (HEIGHT*0.66) >> 0;
    canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
    canvasCtx.lineWidth = 1;

    for(var i = 0; i < count; i++ ){                    
        circles.push(i/count*circleMaxWidth)
    }

    draw = function() {
        canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
        analyser.getByteFrequencyData(frequencyData);
        drawVisual = requestAnimationFrame(draw);
        for(var i = 0; i < circles.length; i++) {
            var v = frequencyData[i] / 128.0;
            var y = v * HEIGHT / 2;
            var circle = circles[i];
            canvasCtx.beginPath();
            canvasCtx.arc(WIDTH/2,HEIGHT/2, y/2, Math.PI * 2, false);
            canvasCtx.stroke()
        }                        
        
    };
    draw();                    
}
//柱形条
else if (visualSetting == "bar") {
    analyser.fftSize = 256;
    var bufferLength = analyser.frequencyBinCount;
    console.log(bufferLength);
    var dataArray = new Uint8Array(bufferLength);
    canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);

    var gradient = canvasCtx.createLinearGradient(0, 0, 0, 200);
    gradient.addColorStop(1, '#0f0');
    gradient.addColorStop(0.5, '#ff0');
    gradient.addColorStop(0, '#f00');
    var barWidth = 10;
    var gap = 2; //间距
    var capHeight = 2;//顶部高度
    var capStyle = '#fff';
    var barNum = WIDTH / (barWidth + gap); //bar个数
    var capYPositionArray = [];
    var step = Math.round(dataArray.length / barNum); 

    draw = function() {
        drawVisual = requestAnimationFrame(draw);
        analyser.getByteFrequencyData(dataArray);
        canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);

        for (var i = 0; i < barNum; i++) {
            var value = dataArray[i * step];
            if (capYPositionArray.length < Math.round(barNum)) {
                capYPositionArray.push(value);
            };
            canvasCtx.fillStyle = capStyle;
            //顶端帽子
            if (value < capYPositionArray[i]) {
                canvasCtx.fillRect(i * 12, HEIGHT - (--capYPositionArray[i]), barWidth, capHeight);
            } else {
                canvasCtx.fillRect(i * 12, HEIGHT - value, barWidth, capHeight);
                capYPositionArray[i] = value;
            };
            canvasCtx.fillStyle = gradient;//渐变
            canvasCtx.fillRect(i * 12 , HEIGHT - value + capHeight, barWidth, HEIGHT-2); //绘制bar
        }
    };
    draw();
}

完整代码 github

免责声明:文章转载自《HTML5 getUserMedia/AudioContext 打造音谱图形化》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇android 在子线程中使用handler更新界面ABAP-VOFM FOR MM-PO PRICE下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

MySQL 错误日志(Error Log)

    同大多数关系型数据库一样,日志文件是MySQL数据库的重要组成部分。MySQL有几种不同的日志文件。通常包括错误日志文件,二进制日志,通用日志,慢查询日志,等等。 这些日志能够帮助我们定位mysqld内部发生的事件,数据库性能故障,记录数据的变更历史,用户恢复数据库等等。本文主要描写叙述错误日志文件。   1、MySQL日志文件系统的组成   a...

javascript 未来新方法的介绍

JavaScript1.6数组方法的扩展 var tmp = [1, 2, 3, 4, 5, 3]; console.log('indexOf:找出数组中某一对象的下标,如果没有则为-1'); console.log(tmp.indexOf(3) == 2); console.log(tmp....

go语言基础学习

go基础学习,面向对象-方法在Go语言中,可以给任意自定义类型(包括内置类型,但不包括指针类型)添加相应的方法 使用= 和:=的区别: // = 使用必须使用先var声明例如: var a a=100 //或 var b = 100 //或 var c int = 100 // := 是声明并赋值,并且系统自动推断类型,不需要var关键字 d :=...

前端常见跨域解决方案(全)

什么是跨域? 跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。 跨域的根本原因就是浏览器,所以在后台是不存去前端一样上的跨域的,后台调后台是不会跨域的,但是必须网络是通的 广义的跨域: 1.) 资源跳转: A链接、重定向、表单提交 2.) 资源嵌入: <link>、<script>、<img>...

(转载)Linux定时任务cron配置

(转载)http://blog.csdn.net/jbgtwang/article/details/7995801 实现linux定时任务有:cron、anacron、at等,这里主要介绍cron服务。 名词解释:    (1)cron是服务名称;    (2)crond是后台进程;    (3)crontab则是定制好的计划任务表; 软件包安装: 要使用...

HTML5+CSS3学习笔记(二)

学习笔记一只是大致介绍了一下HTML的相关内容以及简单的框架,接下来就是学习HTML的各种标签。HTML的标签有很多,简单的介绍可以参照这个网址(http://www.w3school.com.cn/tags/html_ref_byfunc.asp)。 HTML5作为最新的HTML标准,毫无疑问发生了某些变化,增加了很多标签,也修改了一些标签,不过大致的结...