当前位置: 首页 > 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>

相关文章:

  • 2021年蓝桥杯第十二届CC++大学B组真题及代码
  • 计算机工具基础(七)——Git
  • 【电气设计】接地/浮地设计
  • 前端知识点 --- 事件监听器(javascript)
  • Windows 常用快捷键
  • 数学概念学习
  • kube-vip实践
  • C# 正则表达式
  • WSL2增加memory问题
  • 开源视觉语言模型MiniMax-VL-01:动态分辨率+4M超长文本,性能比肩GPT-4o
  • 用 Python 也能做微服务?
  • 搭建小程序该如何选择服务器?
  • 恒流源电路深度解析:各类架构的优缺点与应用场景
  • C++14 新增的特性
  • 深入解析 Java GC 调优:减少 Minor GC 频率,优化系统吞吐
  • 数据结构篇:空间复杂度和时间复杂度
  • HarmonyOS NEXT开发实战——TypeScript快速入门与ArkTS介绍
  • go中的文件、目录的操作
  • 编程题记录3
  • 算法训练营第二十三天 | 贪心算法(一)
  • 人民日报读者点题:规范涉企执法,怎样防止问题反弹、提振企业信心?
  • “冲刺万亿城市”首季表现如何?温州领跑,大连GDP超徐州
  • 重新认识中国女性|婚姻,自古以来就是一桩生意
  • 玉渊谭天丨“稀土管制让美国慌了”,美军工稀土储备仅够数月
  • 明查|把太平洋垃圾污染问题甩锅中国,特朗普用的是P过的图
  • 最大规模的陈逸飞大展启幕:回望他,回望一个时代