用原生 Audio API 实现一个千千静听

【前端】Web Audio API接口介绍 记录下Web Audio API接口的介绍。 Web Audio API 提供了在 Web 上控制音频的一个非常有效通用的系统,允许开发者来自选音频源,对音频添加特效,使音频可视化,添加空间效果(如平移),等等。 阅读详情

前言

哈喽,大家好,我是海怪。

最近看了一下钟文泽的 Macbook Pro 测评视频(唉,最近又想买电子产品了),他在测评音响的时候,点播了一首蔡琴的《渡口》。

当听到这首歌的时候,我真的是情不自禁地感叹: “爷青回!!”,想当年,第一次听这首歌的时候还是在 Windows XP 系统上的 千千静听 这个播放器里听到的,那时印象最深刻的就是里面的 音频可视化(频谱图) 了。

当我在发呆、无聊的时候,音频频谱图里的小浮块总能让我盯上一整天。而如今,在各大音乐软件中已很少看到这样的频谱图了。那今天就跟大家一起用原生的 Audio API 来实现这个频谱图吧。

项目已经放在 Github,也可以在 这里预览

由于原来的拟物风格实现太难实现了,只能做个粗糙的版本 😃

解决思路

首先我们要理解频谱图里的这些“长条”是什么意思。实际上这是音频里的 频率 Frequency,我们常说的低音炮和美高音就是指在声音在低频区和高频区的表现。

了解了音频频率后,我们可以先理清一下这个小玩具的实现思路:

从音频获取音频流 stream,通过中间的解析器分析出频率值 freqency,将这些频率值通过“长条”的方式绘制在 <canvas> 上,然后以此不断循环就可以实现这样的频谱动态图了。

根据上面的思路,我们首先要准备好这样的页面结构:

const Player: FC = () => {
  const {visualize} = useAudioVisualization('#canvas', 50);

  const audioRef = useRef<HTMLAudioElement>(null);

  const onPlay = async () => {
    if (audioRef.current) {
      await audioRef.current.play();
      const stream = (audioRef.current as any).captureStream();
      visualize(stream)
    }
  }

  return (
    <div className={styles.player}>
      <div className={styles.canvas}>
        <canvas id="canvas" width={500} height={300}/>
      </div>
      <div className={styles.controls}>
        <audio ref={audioRef} src={audioUrl} onPlay={onPlay} controls />
      </div>
    </div>
  )
}

useAudioVisualization

这里使用 React Hook 的方式来封装可视化逻辑:

const useAudioVisualization = (selector: string, length = 50) => {
  // 开始可视化
  const visualize = (stream: MediaStream) => {
  
  }

  return { visualize };
}

visualize

在拿到音频的流之后,我们就可以调用 Audio API 来创建解析器并分析音频了。

// 开始可视化
const visualize = (stream: MediaStream) => {
  const canvasEl: HTMLCanvasElement | null = document.querySelector(selector);
  if (!canvasEl) {
    throw new Error('找不到 canvas');
  }

  // 创建解析器
  audioCtxRef.current = new AudioContext()
  analyserRef.current = audioCtxRef.current.createAnalyser();

  // 获取音频源
  const source = audioCtxRef.current.createMediaStreamSource(stream);
  // 将音频源连接解析器
  source.connect(analyserRef.current);

  // 准备数据数组
  analyserRef.current.fftSize = 256;
  const bufferLength = analyserRef.current.frequencyBinCount;
  const dataArray = new Uint8Array(bufferLength);

  // 开始递归画图
  drawEachFrame(canvasEl, dataArray);
}

这里主要做了几件事:

  • 通过 AudioContext 创建 analyser
  • 将音频输入源连接 analyser,每次播放的时候,音频都会经过 analyser 进行处理
  • 设置 fft,从 analyser 获取音频频率数据 dataArray

经过上面的操作我们已经拿到了音频的数据,接下来就是渲染 <canvas> 的时候了,开始实现 drawEachFrame。

drawEachFrame

我们日常所看到的动画本质上都是一个画面一个画面连续播放的效果。

只要画面足够快就可以让画面动起来,那究竟要多快呢?相信有的同学已经开始拿起纸和笔来算了。

其实并不用这么复杂,这里给大家推荐一个 API requestAnimationFrame。它会以浏览器的显示频率来作为其动画动作的频率,比如浏览器每 10ms 刷新一次,动画回调也每 10ms 调用一次,这样就不会存在过度绘制的问题,动画不会掉帧,自然流畅。

只要我们在 requestAnimationFrame 的 callback 里不断地绘制 <canvas> 就可以获得一个流畅的频谱图了。

// 每个动画帧都画图
const drawEachFrame = (canvasEl: HTMLCanvasElement, dataArray: Uint8Array) => {
  // 递归调用
  requestAnimateFrameIdRef.current = requestAnimationFrame(() => drawEachFrame(canvasEl, dataArray));

  if (analyserRef.current) {
    // 读取当前帧新的数据
    analyserRef.current.getByteFrequencyData(dataArray);
    // 更新长度
    const bars = dataArray.slice(0, Math.min(length, dataArray.length));
    // 画图
    clearCanvas(canvasEl);
    // 绘制小浮块
    drawFloats(canvasEl, bars);
    // 绘制条状图
    drawBars(canvasEl, bars);
  }
}

上面的 drawEachFrame 里又调用了一次 requestAnimationFrame,以此来实现递归循环调用的效果。

这里我们还会把 requestAnimateFrameId 给记录下来,以防之后销毁时要调用 window.cancelAnimationFrame(id) 来清除。

clearCanvas

在绘制 <canvas> 前,我们先把它给清空一下:

export const clearCanvas = (canvasEl: HTMLCanvasElement) => {
  const canvasWidth = canvasEl.width;
  const canvasHeight = canvasEl.height;
  const canvasCtx = canvasEl.getContext("2d");

  if (!canvasCtx) {
    return;
  }

  // 绘制图形
  canvasCtx.fillStyle = 'rgb(29,19,62)';
  canvasCtx.fillRect(0, 0, canvasWidth, canvasHeight);
}

这样就能得到一个纯色的 “白板” 了:

drawBars

接下来实现条状图,图示:

代码实现:

// 浮动的小块
let floats: any = [];
// 高度
const FLOAT_HEIGHT = 4;
// 下落高度
const DROP_DISTANCE = 1;
// Bar 的 border 宽度
const BAR_GAP = 2;

export const drawBars = (canvasEl: HTMLCanvasElement, dataArray: Uint8Array) => {
  const canvasWidth = canvasEl.width;
  const canvasHeight = canvasEl.height;
  const canvasCtx = canvasEl.getContext("2d");

  if (!canvasCtx) {
    return;
  }

  const barWidth = canvasWidth / dataArray.length
  let x = 0;

  dataArray.forEach((dataItem) => {
    const barHeight = dataItem;

    // 添加渐变色
    const gradient = canvasCtx.createLinearGradient(canvasWidth / 2, canvasHeight / 2, canvasWidth / 2, canvasHeight);
    gradient.addColorStop(0, '#68b3ec');
    gradient.addColorStop(0.5, '#4b5fc9');
    gradient.addColorStop(1, '#68b3ec');

    // 画 bar
    canvasCtx.fillStyle = gradient;
    canvasCtx.fillRect(x, canvasHeight - barHeight, barWidth, barHeight);

    x += barWidth + BAR_GAP;
  })
}

这里有几个点要注意:

  • 画长方形的时候,原点是在左上角,所以 y 的值为 canvasHeight - barHeight,即 总高度 - 条形高度
  • 画下一个 bar 的时候,需要 + BORDER_WIDTH 来空出一个空隙,不然 bar 就都黏在一起了
  • 在 <canvas> 中画渐变,需要用 addColorStop 来实现

最后效果:

drawFloats

有了上面画条状 bar 的经验后,我们很容易就能想到怎么画这些小块了:

图示:

export const drawFloats = (canvasEl: HTMLCanvasElement, dataArray: Uint8Array) => {
  const canvasWidth = canvasEl.width;
  const canvasHeight = canvasEl.height;
  const canvasCtx = canvasEl.getContext("2d");

  if (!canvasCtx) {
    return;
  }

  // 找到最大值,以及初始化高度
  dataArray.forEach((item, index) => {
    // 默认值
    floats[index] = floats[index] || FLOAT_HEIGHT;
    // 处理当前值
    const pushHeight = item + FLOAT_HEIGHT;
    const dropHeight = floats[index] - DROP_DISTANCE;
    // 取最大值
    floats[index] = Math.max(dropHeight, pushHeight);
  })

  const barWidth = canvasWidth / dataArray.length;
  let x = 0;

  floats.forEach((floatItem: number) => {
    const floatHeight = floatItem;

    canvasCtx.fillStyle = '#3e47a0';
    canvasCtx.fillRect(x, canvasHeight - floatHeight, barWidth, FLOAT_HEIGHT);

    x += barWidth + BAR_GAP;
  })
}

这里最关键就是这个小浮块的高度,我们直接取浮想块下降了的高度 dropHeight 以及被 bar 推高的高度 pushHeight 他们两的最大值就可以了 floats[index] = Math.max(dropHeight, pushHeight)。

在实现好了之后,来一首试音原声大碟《渡口》,即可享受频谱图带来的快乐:

stopVisualize

有开始就有结束,由于这里动用了 <canvas>, requestAnimationFrame 这些资源,所以当组件销毁时应该清空他们:

const useAudioVisualization = (selector: string, length = 50) => {
  ...

  // 重置 canvas
  const resetCanvas = () => {
    const canvasEl: HTMLCanvasElement | null = document.querySelector(selector);
    if (canvasEl) {
      const emptyDataArray = (new Uint8Array(length)).map(() => 0);
      clearFloats();
      clearCanvas(canvasEl);
      drawFloats(canvasEl, emptyDataArray);
    }
  }

  // 停止
  const stopVisualize = () => {
    if (requestAnimateFrameIdRef.current) {
      window.cancelAnimationFrame(requestAnimateFrameIdRef.current);
      resetCanvas();
    }
  };

  return {
    visualize,
    stopVisualize,
    resetCanvas,
    requestAnimateFrameId: requestAnimateFrameIdRef.current
  };
}

这里我们也把 requestAnimateFrameId 扔出来,可由开发者自己处理。

完整的使用方式是这样的:

const Player = () => {
  const {visualize, stopVisualize, resetCanvas} = useAudioVisualization('#canvas', 50);
  
  const audioRef = useRef<HTMLAudioElement>(null);

  const onPlay = async () => {
    if (audioRef.current) {
      stopVisualize();
      await audioRef.current.play();
      const stream = (audioRef.current as any).captureStream();
      visualize(stream)
    }
  }

  const onPause = async () => {
    resetCanvas();
  }

  useEffect(() => {
    resetCanvas();
    return () => {
      stopVisualize()
    }
  }, []);

  return (
    <div className={styles.player}>
      <div className={styles.canvas}>
        <canvas id="canvas" width={500} height={300}/>
      </div>
      <div className={styles.controls}>
        <audio ref={audioRef} src={audioUrl} onPlay={onPlay} onPause={onPause} controls />
      </div>
    </div>
  )
}

更好看的样式就交给同学们自己实现了 😃 当然你也可以在 我的 Github 项目 里直接 Copy 我的丑陋样式。

总结

最后总结一下这个频谱图的实现:

  1. 使用 Audio API 创建 analyser,将音频流 stream 连接到 analyser
  2. 设置 analyser 的 fft 参数,以此获取音频数据
  3. 通过递归调用 requestAnimationFrame 来实现动画效果
  4. 使用 Canvas API 来绘制条形图以及小浮块,将这绘制操作放在 requestAnimationFrame 的回调中,从而展示动态的频谱图

如果你看完还是做不出自己的千千静听,可以在 我的 Github 项目 里直接看源码实现。

好了,这个千千静听的项目就给大家带到这里。如果你喜欢我的文章,可以走一波关注,一键三连我也不介意,比心 ❤️

HTML5 Audio API在音乐应用中的实用功能及其集成与使用 通过创建Audio对象,加载和播放音频文件,开发者可以控制音频的播放、暂停、停止等操作,并实时获取音频的播放进度和状态。未来,随着技术的不断进步和需求的不断变化,我们有理由相信HTML5 Audio API将在音乐应用领域中发挥更加重要的作用,为用户带来更加精彩和创新的音频体验。例如,可以结合CSS和HTML来创建美观的用户界面,使用JavaScript来实现复杂的逻辑控制和交互效果,以及利用Web Audio API或其他音频处理库来进行音频数据的分析和处理。的Audio对象,并指定了要播放的音频文件。 阅读详情

相关推荐

【前端】使用Web Audio API 技术播放音乐

音频节点通过它们的输入输出相互连接,形成一个链或者一个简单的网。一般来说,这个链或网起始于一个或多个音频源。这些节点的输出可以连接到其他节点的输入上,然后新节点可以对接收到的采样数据再进行其他的处理,再形成一个结果流。最后节点流再输出。音频节点就是 AnalyserNode、GainNode 、AudioContext 对象等。

qq_2511276124的博客 5625

Audio API 实现音频播放器

市面上实现音频播放器的库有很多,比如等等,但是都不支持大音频文件处理,100多M的文件就有可能导致浏览器崩溃。总之和我目前的需求不太符合,所以打算自己实现一个音频播放器,这样不管什么需求 在技术上都可控。下面我们简单介绍下、和howlerJs的实现,然后再讲解如何利用audio API实现自定义语音播放器。具体资源github下载。

woyebuzhidao321的博客 2868

HTML5中Audio API在音乐应用中的使用技巧有哪些?

Audio API还支持音频流的处理与合成,通过AudioNode接口和AudioContext的createScriptProcessor方法,开发者可以对音频流进行实时处理,如音效添加、混音等。此外,Audio API还提供了诸如loadedmetadata、play、pause、ended等事件,开发者可以通过监听这些事件来处理音频的加载、播放、暂停和结束等状态变化。Audio API还提供了音频分析的功能,通过AnalyserNode接口,开发者可以获取音频的频谱数据,从而实现音频可视化效果。

aronSandy的博客 1155

【Web技术】1505- Web Audio API 太强了,让我们一起领略音频之美

在浏览器中,我们通常使用 audio 标签来播放音频:<audiocontrols> <sourcesrc="myAudio.mp3"type="audio/mpeg"> <sourcesrc="myAudio.ogg"type="audio/ogg"> </audio>虽然 audio 标签使用起来很简单,但也存在一些局限。比如它...

pingan8787 1722

Ubuntu下运行Windows软件:Deepin-wine移植原理与实战指南

Wine作为Linux上运行Windows应用的兼容层,通过动态链接库替换实现Win32 API到POSIX的翻译,无需虚拟化即能获得接近原生的性能。然而标准Wine对国内常用软件支持有限,依赖配置繁琐。Deepin-wine是深度团队基于Wine优化而成的分支,预置了字体替换、注册表优化和音频编解码器等运行时组件,尤其适合微信、QQ等国产软件的移植部署。在Ubuntu/Debian系统中,通过引入i386架构、安装libwine等基础依赖,并配置WINEARCH与WINEPREFIX,即可快速搭建Wind

weixin_26976635的博客 253

C# 千千静听 源码解析与学习指南

在C#中,类是一种引用类型,用于描述具有相同属性和方法的对象集合。类是创建对象的蓝图,定义了对象的状态和行为。下面是一个简单的C#类定义示例:// 属性set;set;// 方法创建对象的过程称为实例化。在上述类定义的基础上,创建一个AudioFile类的实例,代码如下:// 输出: Playing the audio file: ExampleSong.mp3。

weixin_35755188的博客 787

爷青回!用原生 Audio API 实现一个千千静听

前言最近看了一下钟文泽的 Macbook Pro 测评视频(唉,最近又想买电子产品了),他在测评音响的时候,点播了一首蔡琴的《渡口》。当听到这首歌的时候,我真的是情不自禁地感叹:“爷青回!...

Yvette Lau的专栏 816

千千静听(vs2008 C# 源码)

千千静听(vs2008 C# 源码)。。。。。。。

千千静听v5.7.9纯本地增强版:断网音频播放器技术实现

本地音频播放器是Windows桌面环境中基础但关键的多媒体组件,其核心在于脱离网络依赖、保障隐私安全与系统兼容性。原理上需实现进程级隔离、网络栈封禁与音频子系统直通,技术价值体现在零后台、零上报、零广告的纯净体验,以及对老旧硬件(如Conexant声卡)、旧系统(Win7/Win10 LTSC)和高隐私场景(档案数字化、播客剪辑)的可靠支撑。尤其在ID3v2.4标签解析、DPI缩放适配、WDM驱动兼容等细节上,纯本地增强方案通过PE结构修改、API Hook与底层音频API重定向等手段完成系统级修复。千千静

weixin_32822843的博客 351

易语言高仿千千静听源码可播放音乐.zip

易语言高仿千千静听源码可播放音乐.zip 开发语言:易语言5.93 模块依赖:无需其他模块ec

crazyswarm无人机集群搭建笔记(一)资料索引

crazyswarm无人机搭建笔记:资料收集

Coco_range的博客 808

千千静听v5.7.9增强版:本地音频资产治理框架

音频处理不仅是播放与格式转换,更涉及元数据可信固化、离线可审计性与文件级数字凭证构建。在云服务存在同步延迟、隐私风险与依赖不确定性的背景下,本地化音频处理链路设计成为科研归档、非遗保护、司法取证等高信度场景的核心需求。本文聚焦‘纯本地音频框架’这一技术范式,解析其如何通过编译裁剪、资源内嵌、配置校验与消息拦截实现零网络依赖,并支撑批量元数据固化、离线波形缓存、播放行为审计等硬核能力,为弱网环境、嵌入式终端及隐私敏感型音频资产管理提供稳定、可复现、可长期验证的工程落地方案。

weixin_29168675的博客 267

海康威视工业相机SDK二次开发实战:多相机同步采集与图像存储

本文详细介绍了海康威视工业相机SDK二次开发实战,包括多相机同步采集与图像存储的优化方案。通过MVS软件搭建开发环境,实现硬件与软件触发同步采集,并提供高效的图像存储管理技巧,帮助开发者提升工业视觉项目的效率与稳定性。

weixin_29168813的博客 666

千千静听v5.7.9纯本地增强版:轻量级音频播放器的技术复刻与工程优化

本地音频播放器是数字音频处理的基础工具,其核心在于低延迟解码、零依赖运行与资源可控性。基于经典架构的深度优化,如硬解加速、标签编码兼容性修复、快捷键防抖及可视化插件沙箱化,显著提升稳定性与专业适配能力。这类纯本地设计规避了云服务耦合与后台冗余,特别适用于DAW监听、工业声学监测、老旧设备部署等对确定性响应和离线可靠性要求严苛的场景。千千静听v5.7.9增强版正是这一技术路径的典型实践,融合MFC遗产代码重构、汇编级网络模块剥离与ASIO低延迟调优,成为高保真音频链路中不可替代的‘哑终端’节点。

weixin_29698641的博客 265

从Multibit到Debanking:芯片设计中的时序优化完整流程解析(含真实案例)

本文深入解析芯片设计中Multibit技术与Debanking的时序优化完整流程,结合7nm移动SoC真实案例,详细探讨Multibit的核心优势、实施条件及潜在风险,并提供Debanking的触发时机与操作策略。通过平衡Multibit与Debanking,工程师可有效优化PPA(Power-Performance-Area),提升芯片设计效率。

weixin_30316097的博客 460

读取rosbag中的IMU信息并转为tum格式的csv和txt

rostopic -b xx.bag -p /xx > xxx.txt #for euroc structvio import os, sys,re,csv name="street_07" # basedir="/media/yinjie/2498AF1F98AEEF0C/adataset/15results/imu_bagtxt/"+name+'.txt' # txtdir="/media/yinjie/2498AF1F98AEEF0C//adataset/15results/imu_o

qq_42928559的博客 1919

audio基础api

audio基础api调用 参考博客 <audio>的很多方法对<video>同样适用 audio基本使用案例 <audio controls> <source src="audiofile.mp3" type="audio/mpeg"> <source src="audiofile.ogg" type="audio/ogg"> ...

Dj_Fairy的博客 1792

TortoiseGit提示No supported authentication methods available错误

一、问题 在使用TortoiseGit与远程仓库进行同步代码的时候提示如下错误,即:没有支持认证的方法可用。而GitBash却可以正常使用,不受影响。 通过调查,发现应该是TortoiseGit远程仓库的公匙无法和本地的密匙进行匹配认证造成的(主要是TortoiseGit缺少本地密匙)。由于TortoiseGit的默认网络SSH client是TortoiseGitPlink.exe,如下图所示。因此主要有两种方式来解决该问题: 不修改TortoiseGit的默认网络SSH client,此时需要为To

Jeffxu_lib的博客 2万+

Web Audio API 太强了,让我们一起领略音频之美

在浏览器中,我们通常使用 audio 标签来播放音频:<audiocontrols> <sourcesrc="myAudio.mp3"type="audio/mpeg"> <sourcesrc="myAudio.ogg"type="audio/ogg"> </audio>虽然 audio 标签使用起来很简单,但也存在一些局限。比如它...

frontend_frank的博客 812

AD620单电源应变片测量电路分析

笔者最近在用AD620做应变片的压力检测电路,正好AD620的datasheet中有对于应变片压力测量的应用电路,参考该电路进行设计及实验后发现该电路存在些许问题,并对其进行了优化改进。本文纯属个人理解,供大家参考,欢迎批评指正。 AD620应用电路分析及优化 应用电路分析 AD620手册中原电路如下(加了部分标注以便分析): 该电路中,前半部分比较明显,为经典的AD620放大电路结构:四个应变...

拙诚电子工作室 1万+

RealSense D435i深度相机实战:从像素坐标到三维空间的精准映射(Python实现)

本文详细介绍了使用RealSense D435i深度相机实现从二维像素坐标到三维空间坐标精准映射的两种Python实战方法:深度图反投影法与点云直接查找法。文章涵盖环境搭建、相机对齐、代码实现、性能对比及与YOLO等检测模型结合的进阶应用,并提供了关键的避坑指南和参数调优建议,帮助开发者在机器人、三维重建等项目中高效应用。

weixin_29291573的博客 382

【190320】C# 写的简易千千静听播放器源代码

源码下载简介 C# 写的简易千千静听播放器,类似千千静听播放器,支持歌曲分类列表播放。 源码下载地址:点击下载 备用下载地址:点击下载

资源小站 917

计算电磁学(三)有限差分方法

有限差分方法(FDM)是一种数值处理方法,通过将偏微分方程变换为一组代数方程,通过求解代数方程组来得到原边值问题的近似值。由于有限差分法非常简单,所以也特别的稳定高效。其中时域有限差分方法(FDTD)是电磁分析中的常用方法。在计算电磁学中,常用有限差分方法(finite difference equations)来替代微分方程。一个有限差分法通常包括三个步骤将求解域划分网格用有限差分方程逼近给定的微分方程求解在给定边界条件和初始状态下的方程常用的网格模型方阵斜方阵三角阵圆形阵。

CCC_B的博客 1639

HTML5 <Audio>标签API整理(一)

 简单实例: &lt;audio id="myAudio"&gt;&lt;/audio&gt; &lt;script&gt; var myAudio = document.getElementById('myAudio'); myAudio.src = '../content/audio/海阔天空.mp3'; myAudio.pla...

weixin_33861800的博客 832

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

【190302】VC 仿千千静听音乐播放器源码源代码

源码下载简介 超酷界面的VC++音乐播放器,推荐给大家, 在VC++6.0下顺利编译,它的界面非常 酷,编译中也没有发现用到第三方的界面控件,而且播放器的窗口还具有像千千静听一样的自动吸附功能,也就是拖动窗口从远处回来的时候它会自动吸附到播放器的其它窗口上,它还支持拖放添加音乐列表,支持歌词显示,绝对值得一看。 源码下载地址:点击下载 备用下载地址:点击下载 ...

资源小站 1601

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

web audio api 实现音频播放

最近被选中做音视频,挺幸运的吧,一直在接触新的项目,每次都能被分到新的项目组,干好多费头发的事情😂 上周五肝到12点半,总算是把音频编辑上了线 总结了一下,决定写一写,也盘点一下遇到的坑 web audio API是 HTML5新增的API,提供了在web上控制音频的一个有效通用的系统,开发者可以自选音频源,对音频添加特效,添加空间效果,使音频可视化,等等。

鲸落 3599

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

上一篇: 用JS轻松实现一个录音、录像、录屏的工具库
下一篇: 来聊聊我们为什么要写单测
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

写代码的海怪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值