当前位置: 首页 > news >正文

音频录制小妙招-自制工具-借助浏览器录一段单声道16000采样率wav格式音频

先看效果

1、打开页面

2、点击开始录音,弹出权限提示,点击“仅这次访问时允许”

3、录完后,点击停止

4、文件自动下载到默认目录

上代码

 js 部分

document.addEventListener('DOMContentLoaded', () => {
    const startBtn = document.getElementById('startBtn');
    const stopBtn = document.getElementById('stopBtn');
    const audioPlayback = document.getElementById('audioPlayback');

    let mediaRecorder;
    let audioChunks = [];

    startBtn.addEventListener('click', async () => {
        try {
            // Request access to the microphone
            const stream = await navigator.mediaDevices.getUserMedia({ audio: { channelCount: 1, sampleRate: 16000 } });
            
            if (!stream) {
                throw new Error('No media stream received.');
            }

            // Create a MediaRecorder instance with specific settings
            mediaRecorder = new MediaRecorder(stream, {
                mimeType: 'audio/webm'
            });

            mediaRecorder.ondataavailable = event => {
                audioChunks.push(event.data);
            };

            mediaRecorder.onstop = () => {
                const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
                convertWebmToWav(audioBlob);
                audioChunks = [];
            };

            mediaRecorder.start();
            startBtn.disabled = true;
            stopBtn.disabled = false;
        } catch (err) {
            console.error('Error accessing microphone:', err);
            alert('Error accessing microphone: ' + err.message);
        }
    });

    stopBtn.addEventListener('click', () => {
        if (mediaRecorder && mediaRecorder.state !== 'inactive') {
            mediaRecorder.stop();
        }
        startBtn.disabled = false;
        stopBtn.disabled = true;
    });

    function convertWebmToWav(webmBlob) {
        const reader = new FileReader();
        reader.onloadend = () => {
            const arrayBuffer = reader.result;
            const audioContext = new AudioContext();

            audioContext.decodeAudioData(arrayBuffer, audioBuffer => {
                const samples = audioBuffer.getChannelData(0);
                const buffer = createWav(samples, audioBuffer.sampleRate);
                const blob = new Blob([buffer], { type: 'audio/wav' });
                const audioUrl = URL.createObjectURL(blob);
                audioPlayback.src = audioUrl;

                // Save or download the blob as a WAV file
                const link = document.createElement('a');
                link.href = audioUrl;
                link.download = 'recorded_audio.wav';
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
            }, error => {
                console.error('Error decoding audio data:', error);
            });
        };
        reader.readAsArrayBuffer(webmBlob);
    }

    function createWav(samples, sampleRate) {
        const buffer = new ArrayBuffer(44 + samples.length * 2);
        const view = new DataView(buffer);

        // RIFF identifier
        writeString(view, 0, 'RIFF');
        // file length minus RIFF identifier length and file description length
        view.setUint32(4, 36 + samples.length * 2, true);
        // RIFF type
        writeString(view, 8, 'WAVE');
        // format chunk identifier
        writeString(view, 12, 'fmt ');
        // format chunk length
        view.setUint32(16, 16, true);
        // sample format (raw)
        view.setUint16(20, 1, true);
        // channel count
        view.setUint16(22, 1, true);
        // sample rate
        view.setUint32(24, sampleRate, true);
        // byte rate (sample rate * block align)
        view.setUint32(28, sampleRate * 2, true);
        // block align (channel count * bytes per sample)
        view.setUint16(32, 2, true);
        // bits per sample
        view.setUint16(34, 16, true);
        // data chunk identifier
        writeString(view, 36, 'data');
        // data chunk length
        view.setUint32(40, samples.length * 2, true);

        floatTo16BitPCM(view, 44, samples);

        return buffer;
    }

    function floatTo16BitPCM(output, offset, input) {
        for (let i = 0; i < input.length; i++, offset += 2) {
            const s = Math.max(-1, Math.min(1, input[i]));
            output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
        }
    }

    function writeString(view, offset, string) {
        for (let i = 0; i < string.length; i++) {
            view.setUint8(offset + i, string.charCodeAt(i));
        }
    }
});



html部分

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Audio Recorder</title>
</head>
<body>
    <h1>Audio Recorder</h1>
    <button id="startBtn">Start Recording</button>
    <button id="stopBtn" disabled>Stop Recording</button>
    <br><br>
    <audio id="audioPlayback" controls></audio>

    <script src="recorder.js"></script>
</body>
</html>

相关文章:

  • ARM架构薄记2——ARM学习架构抓手(以ARMv7为例子)
  • 元音辅音及其字母组合发音
  • 基于STM32进行FFT滤波
  • Python 常用内建模块-urllib
  • LINUX基础 [二] - 进程概念
  • 简单实用!百度AI + Raphael AI = 免费生图
  • CSS 中flex - grow、flex - shrink和flex - basis属性的含义及它们在弹性盒布局中的协同作用。
  • 以“无敏”理念守护婴童健康成长,Witsbb健敏思获京东健康“新锐突破奖”
  • [笔记.AI]多头自注意力机制(Multi-Head Attention)
  • C# 元组
  • 【图像生成之十八】Seedream 2.0
  • 计算机网络总结
  • OpenHarmony 开源硬件学习全指南:从入门到实战
  • 2Dslam前端分类
  • Vue3+UniApp:在单个页面实现固定 TabBar 的多种方式
  • Vue学习笔记集--路由
  • nginx配置https域名后,代理后端服务器流式接口变慢
  • Kubernetes的Replica Set和ReplicaController有什么区别
  • 计划管理工具应该具备的能(甘特图)
  • Qt高分屏自适应
  • 青年与人工智能共未来,上海创新创业青年50人论坛徐汇分论坛举办
  • 乡村快递取件“跑腿费”屡禁不止?云南元江县公布举报电话
  • A股低开高走全线上涨:军工股再度领涨,两市成交12934亿元
  • 汪明荃,今生不负你
  • 胖东来发布和田玉、翡翠退货说明:不扣手续费等任何费用
  • 一季度全国消协组织为消费者挽回经济损失23723万元