导读:本期聚焦于半夏创作的《TypeScript中如何正确定义Media Recorder Audio Sample Rate API音频采样率类型?》,敬请观看详情。浏览器音频录制接口返回的采样率信息在TypeScript项目里经常被简单写成number类型,这种偷懒的做法会让采样率单位混乱、取值范围失控,甚至埋下运行时错误的隐患。本文从AudioContext和MediaRecorder相关API出发,讲解sampleRate字段的来源与含义,梳理常见的取值范围,并给出字面量联合类型、类型别名、 branded type等多种类型定义方案,对比它们的适用场景与优缺点,同时结合AudioContextOptions、MediaTrackConstraints等配置项演示如何在创建与约束阶段就用类型系统约束采样率,帮助你在音频采集项目中写出更严谨可靠的TypeScript代码。

在浏览器端做音频录制时,采样率(Sample Rate)是一个绕不开的参数。无论是通过MediaRecorder采集音频流,还是用AudioContext对音频做进一步处理,都会接触到sampleRate这个字段。在TypeScript项目中,如果只是简单地把采样率声明为number,类型系统就无法帮你把关,诸如把Hz和kHz搞混、传入非法值这类问题就只能在运行时才暴露。本文围绕如何在TypeScript中为音频采样率定义严谨的类型展开,给出多种方案供不同场景选择。

TypeScript中如何正确定义Media Recorder Audio Sample Rate API音频采样率类型?

一、采样率从哪里来:先弄清API中的sampleRate字段

在浏览器音频相关API中,sampleRate至少出现在三个地方。第一处是AudioContext实例,创建上下文时可以通过AudioContextOptions指定采样率,创建后可以通过context.sampleRate读取实际生效的值,单位是Hz,比如44100、48000。第二处是MediaStreamAudioSourceNodeAudioBuffer,每个AudioBuffer都有自己的sampleRate属性,表示缓冲区中每秒的采样点数。第三处是MediaStreamTrack的getSettings方法,通过track.getSettings().sampleRate可以拿到当前音轨的实际采样率配置。

需要注意一个常见误区:采样率的单位是Hz而不是kHz。Web Audio API规范中所有采样率一律以Hz表示,44100就是常说的44.1kHz。有些开发者习惯把44.1直接当采样率传进去,结果得到的音频速度完全不对。另一个要点是,浏览器并非支持任意采样率,规范要求采样率在8000到96000之间,且不同浏览器、不同设备支持的具体档位可能有差异,如果传入不支持的值,浏览器可能会回退到默认采样率,甚至直接抛出异常。

看一个基础的读取示例:

async function getActualSampleRate(): Promise<number> {
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
  const track = stream.getAudioTracks()[0];
  const settings = track.getSettings();
  // sampleRate 单位是 Hz,例如 48000
  return settings.sampleRate ?? 48000;
}

这段代码在原生DOM类型定义(lib.dom.d.ts)中其实已经有类型支持,MediaTrackSettings.sampleRate被声明为number。但这个number太宽泛了,我们的业务代码完全可以在其之上做更严格的类型约束。

二、用字面量联合类型约束常见采样率档位

音频领域的采样率其实是相对固定的几个档位:8000(电话音质)、16000、22050、44100(CD音质)、48000(专业音频与视频常用)、96000(高解析度音频)。既然取值集合有限,最直接的方案就是用字面量联合类型定义:

type CommonSampleRate = 8000 | 16000 | 22050 | 44100 | 48000 | 96000;

// 创建 AudioContext 时约束传入值
function createContext(rate: CommonSampleRate): AudioContext {
  return new AudioContext({ sampleRate: rate });
}

const ctx1 = createContext(48000); // OK
// const ctx2 = createContext(44100); // OK
// const ctx3 = createContext(44100 / 1000); // 报错,44.1 不在集合中
// const ctx4 = createContext(16000); // OK

这种写法的好处显而易见:编译期就能拦截非法值,编辑器还能给出自动补全提示。缺点是灵活性差,一旦遇到某些设备返回了非标准档位(例如32000),就需要修改类型定义。因此在实际项目中,建议把联合类型用在「主动传入配置」的场景,也就是你自己决定采样率的地方;而从设备读取到的值,由于来源不可控,适合先用宽松类型接收再校验。

一种折中做法是配合类型守卫,把读取到的number收窄为联合类型:

const COMMON_RATES = [8000, 16000, 22050, 44100, 48000, 96000] as const;

function isCommonRate(rate: number): rate is CommonSampleRate {
  return (COMMON_RATES as readonly number[]).includes(rate);
}

function normalizeRate(raw: number | undefined): CommonSampleRate {
  if (raw !== undefined && isCommonRate(raw)) {
    return raw;
  }
  return 48000; // 不在常见档位中时回退到默认值
}

三、用Branded Type表示更宽泛但有边界的采样率

如果业务上需要支持8000到96000之间的任意合法值,字面量联合就不够用了。直接用number又无法表达边界,这时候可以借助branded type(品牌类型),给原始number打上一个「标记」,确保只有经过校验的值才能被当作采样率使用:

declare const SampleRateBrand: unique symbol;

type SampleRate = number & { readonly [SampleRateBrand]: true };

const MIN_RATE = 8000;
const MAX_RATE = 96000;

function createSampleRate(value: number): SampleRate {
  if (!Number.isInteger(value) || value < MIN_RATE || value > MAX_RATE) {
    throw new Error(`非法采样率:${value},必须在 ${MIN_RATE} 到 ${MAX_RATE} 之间的整数`);
  }
  return value as SampleRate;
}

// 业务函数只接受 SampleRate,普通 number 传不进来
function encodeAudio(rate: SampleRate, samples: Float32Array): ArrayBuffer {
  // 编码逻辑,此处省略
  return new ArrayBuffer(0);
}

const rate = createSampleRate(44100);
// encodeAudio(44100, samples)  // 编译报错,必须走校验函数
encodeAudio(rate, new Float32Array(1024)); // OK

branded type的核心思想是「让非法状态不可表示」。任何采样率都必须通过createSampleRate这个入口创建,入口内部完成整数与范围校验,后续所有函数签名都只接受SampleRate类型。这样即使代码规模变大、多人协作,也不会出现有人随手传一个浮点数或越界值的情况。缺点是类型定义稍显晦涩,团队不熟悉时需要一点学习成本,另外它本质上还是number的 структурный类型,绕过类型检查的强制转换依然可能出问题,所以校验函数中的运行时检查不能省略。

四、在MediaRecorder与约束请求中应用类型

调用getUserMedia时,可以通过MediaTrackConstraints中的sampleRatesampleSize表达采集偏好。不过要注意,这两个约束项在Chrome中长期是实验性的,Firefox干脆不支持,所以更稳妥的路径是:用getUserMedia拿到流之后,套一层AudioContext按目标采样率重采样,再通过MediaStreamAudioDestinationNode输出给MediaRecorder。

type TargetRate = 16000; // 语音识别常用 16kHz

const TARGET: TargetRate = 16000;

async function recordAtTargetRate(mimeType: string): Promise<Blob> {
  // 1. 以目标采样率创建上下文
  const ctx = new AudioContext({ sampleRate: TARGET });

  // 2. 采集麦克风原始流
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });

  // 3. 接入上下文并输出到目标节点,实现重采样
  const source = ctx.createMediaStreamSource(stream);
  const dest = ctx.createMediaStreamDestination();
  source.connect(dest);

  // 4. 用重采样后的流录制
  const recorder = new MediaRecorder(dest.stream, { mimeType });
  const chunks: BlobPart[] = [];
  recorder.ondataavailable = (e) => { if (e.data.size > 0) chunks.push(e.data); };

  const done = new Promise<Blob>(resolve => {
    recorder.onstop = () => resolve(new Blob(chunks, { type: mimeType }));
  });

  recorder.start();
  setTimeout(() => recorder.stop(), 5000); // 录5秒
  return done;
}

这个模式在实际项目里非常常用,尤其是语音识别场景往往强制要求16kHz。把TargetRate定义成字面量类型,可以让整个函数链路中的采样率在编译期保持一致。如果还想进一步保证类型安全,可以定义一个泛型工具,把采样率与AudioContext的创建绑定起来。

五、方案选型建议

综合来看,三种方案各有定位。字面量联合类型适合档位固定、由我方控制配置的场景,实现成本最低;branded type适合取值连续但有边界、且采样率会在多个模块间传递的项目,安全性最高;而对设备返回值做接收时,先以numbernumber | undefined接收(因为getSettings返回的sampleRate是可选字段),再通过类型守卫收窄,是最务实的组合拳。

最后提醒两点:一是在单元测试中覆盖采样率校验函数的边界值,包括8000、96000以及越界值;二是别忘了MediaRecorder的mimeType与采样率也会相互影响,例如某些编码格式对采样率档位有额外限制。把类型定义、运行时校验和集成测试结合起来,才能让音频采集模块真正稳定可靠。

TypeScript音频采样率Audio Sample Rate修改时间:2026-09-14 13:39:19

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260914/56718.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。