在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置
1.1 配置module.json5
{"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE_MEDIA","reason": "保存录音文件到媒体库"},{"name": "ohos.permission.READ_MEDIA","reason": "读取录音文件"}],"abilities": [{"name": "EntryAbility","backgroundModes": ["audioRecording"]}]}
}
2. 录音核心功能实现
2.1 录音服务封装
// src/main/ets/service/AudioRecorder.ts
import audio from '@ohos.multimedia.audio';
import fs from '@ohos.file.fs';
import mediaLibrary from '@ohos.multimedia.mediaLibrary';export class AudioRecorder {private audioRecorder: audio.AudioRecorder | null = null;private filePath: string = '';private audioOptions: audio.AudioRecorderOptions = {encoder: audio.AudioEncoder.AAC_LC,sampleRate: audio.AudioSampleRate.SAMPLE_RATE_44100,numberOfChannels: audio.AudioChannel.CHANNEL_2,format: audio.AudioOutputFormat.MPEG_4,uri: '', // 将在startRecording时设置location: { latitude: 0, longitude: 0 } // 可选地理位置};async startRecording(): Promise<boolean> {try {// 创建录音保存路径const context = getContext(this) as Context;const dir = context.filesDir + '/recordings';await fs.ensureDir(dir);this.filePath = `${dir}/recording_${new Date().getTime()}.m4a`;// 初始化录音器this.audioRecorder = await audio.createAudioRecorder();this.audioOptions.uri = `file://${this.filePath}`;// 配置并启动录音await this.audioRecorder.prepare(this.audioOptions);await this.audioRecorder.start();return true;} catch (err) {console.error('启动录音失败:', err);return false;}}async stopRecording(): Promise<string | null> {if (!this.audioRecorder) return null;try {await this.audioRecorder.stop();await this.audioRecorder.release();this.audioRecorder = null;// 保存到媒体库const media = mediaLibrary.getMediaLibrary(getContext(this) as Context);const fileAsset = await media.createAsset(mediaLibrary.MediaType.AUDIO,'recording.m4a',this.filePath);return fileAsset.uri;} catch (err) {console.error('停止录音失败:', err);return null;}}async pauseRecording(): Promise<boolean> {if (!this.audioRecorder) return false;try {await this.audioRecorder.pause();return true;} catch (err) {console.error('暂停录音失败:', err);return false;}}async resumeRecording(): Promise<boolean> {if (!this.audioRecorder) return false;try {await this.audioRecorder.resume();return true;} catch (err) {console.error('恢复录音失败:', err);return false;}}getRecordingState(): audio.AudioState | null {return this.audioRecorder?.getState() || null;}
}
2.2 录音状态管理
// src/main/ets/model/RecordingModel.ts
export class RecordingModel {@Tracked recordingState: 'idle' | 'recording' | 'paused' = 'idle';@Tracked currentDuration: number = 0; // 毫秒@Tracked filePath: string | null = null;private timer: number | null = null;startTimer() {this.stopTimer();this.timer = setInterval(() => {this.currentDuration += 1000;}, 1000);}stopTimer() {if (this.timer) {clearInterval(this.timer);this.timer = null;}}reset() {this.stopTimer();this.recordingState = 'idle';this.currentDuration = 0;this.filePath = null;}
}
3. 用户界面实现
3.1 主录音界面
// src/main/ets/pages/RecordPage.ets
import { AudioRecorder } from '../service/AudioRecorder';
import { RecordingModel } from '../model/RecordingModel';@Entry
@Component
struct RecordPage {private audioRecorder: AudioRecorder = new AudioRecorder();@State recordingModel: RecordingModel = new RecordingModel();build() {Column() {// 录音时间显示Text(this.formatTime(this.recordingModel.currentDuration)).fontSize(40).margin({ top: 50 })// 波形图占位this.buildWaveform()// 录音控制按钮this.buildControlButtons()// 录音文件列表if (this.recordingModel.filePath) {this.buildRecordingInfo()}}.width('100%').height('100%')}@BuilderbuildWaveform() {// 实现波形图显示Canvas(this.context).width('90%').height(150).margin({ top: 30, bottom: 30 }).onReady(() => {// 绘制波形图的逻辑})}@BuilderbuildControlButtons() {Row() {if (this.recordingModel.recordingState === 'idle') {Button('开始录音', { type: ButtonType.Circle }).width(80).height(80).backgroundColor('#FF5722').onClick(() => this.startRecording())} else {if (this.recordingModel.recordingState === 'recording') {Button('暂停', { type: ButtonType.Circle }).width(60).height(60).backgroundColor('#4CAF50').onClick(() => this.pauseRecording())} else {Button('继续', { type: ButtonType.Circle }).width(60).height(60).backgroundColor('#2196F3').onClick(() => this.resumeRecording())}Button('停止', { type: ButtonType.Circle }).width(60).height(60).backgroundColor('#F44336').margin({ left: 30 }).onClick(() => this.stopRecording())}}.margin({ top: 50 })}@BuilderbuildRecordingInfo() {Column() {Text('录音文件:').fontSize(16).margin({ bottom: 10 })Text(this.recordingModel.filePath || '').fontSize(14).fontColor('#666666')Button('播放录音').margin({ top: 20 }).onClick(() => this.playRecording())}.margin({ top: 30 })}async startRecording() {const success = await this.audioRecorder.startRecording();if (success) {this.recordingModel.recordingState = 'recording';this.recordingModel.startTimer();}}async pauseRecording() {const success = await this.audioRecorder.pauseRecording();if (success) {this.recordingModel.recordingState = 'paused';this.recordingModel.stopTimer();}}async resumeRecording() {const success = await this.audioRecorder.resumeRecording();if (success) {this.recordingModel.recordingState = 'recording';this.recordingModel.startTimer();}}async stopRecording() {const filePath = await this.audioRecorder.stopRecording();if (filePath) {this.recordingModel.filePath = filePath;}this.recordingModel.reset();}async playRecording() {// 实现播放录音功能}formatTime(milliseconds: number): string {const totalSeconds = Math.floor(milliseconds / 1000);const minutes = Math.floor(totalSeconds / 60);const seconds = totalSeconds % 60;return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;}
}
3.2 录音列表页面
// src/main/ets/pages/RecordingsListPage.ets
import { mediaLibrary } from '@ohos.multimedia.mediaLibrary';@Entry
@Component
struct RecordingsListPage {@State recordings: Array<mediaLibrary.FileAsset> = [];aboutToAppear() {this.loadRecordings();}async loadRecordings() {try {const media = mediaLibrary.getMediaLibrary(getContext(this) as Context);const fetchOpts: mediaLibrary.MediaFetchOptions = {selections: `${mediaLibrary.FileKey.MEDIA_TYPE}=?`,selectionArgs: [mediaLibrary.MediaType.AUDIO.toString()],order: `${mediaLibrary.FileKey.DATE_ADDED} DESC`};const fetchResult = await media.getFileAssets(fetchOpts);this.recordings = await fetchResult.getAllObject();} catch (err) {console.error('加载录音列表失败:', err);}}build() {List({ space: 10 }) {ForEach(this.recordings, (recording) => {ListItem() {RecordingItem({ recording: recording })}})}.width('100%').height('100%')}
}@Component
struct RecordingItem {private recording: mediaLibrary.FileAsset;build() {Row() {Image($r('app.media.ic_audio')).width(40).height(40).margin({ right: 15 })Column() {Text(this.recording.displayName).fontSize(16)Text(this.formatDate(this.recording.dateAdded * 1000)).fontSize(12).fontColor('#888888')}.layoutWeight(1)Text(this.formatDuration(this.recording.duration)).fontSize(14)}.padding(15)}formatDate(timestamp: number): string {const date = new Date(timestamp);return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;}formatDuration(seconds: number): string {const mins = Math.floor(seconds / 60);const secs = Math.floor(seconds % 60);return `${mins}:${secs.toString().padStart(2, '0')}`;}
}
4. 音频播放功能实现
4.1 音频播放器封装
// src/main/ets/service/AudioPlayer.ts
import audio from '@ohos.multimedia.audio';export class AudioPlayer {private audioPlayer: audio.AudioPlayer | null = null;private currentUri: string = '';async play(uri: string): Promise<boolean> {if (this.audioPlayer && this.currentUri === uri) {await this.audioPlayer.play();return true;}try {await this.stop();this.audioPlayer = await audio.createAudioPlayer();this.currentUri = uri;await this.audioPlayer.reset();await this.audioPlayer.setSource({ source: uri });await this.audioPlayer.play();return true;} catch (err) {console.error('播放失败:', err);return false;}}async pause(): Promise<boolean> {if (!this.audioPlayer) return false;try {await this.audioPlayer.pause();return true;} catch (err) {console.error('暂停失败:', err);return false;}}async stop(): Promise<boolean> {if (!this.audioPlayer) return true;try {await this.audioPlayer.stop();await this.audioPlayer.release();this.audioPlayer = null;this.currentUri = '';return true;} catch (err) {console.error('停止失败:', err);return false;}}async seek(position: number): Promise<boolean> {if (!this.audioPlayer) return false;try {await this.audioPlayer.seek(position);return true;} catch (err) {console.error('跳转失败:', err);return false;}}
}
4.2 播放控制组件
// src/main/ets/components/PlayerControls.ets
@Component
export struct PlayerControls {private player: AudioPlayer;@State isPlaying: boolean = false;@State currentPosition: number = 0;@State duration: number = 0;private updateInterval: number | null = null;build() {Column() {// 进度条Slider({value: this.currentPosition,min: 0,max: this.duration,step: 1,style: SliderStyle.OutSet}).onChange((value: number) => {this.player.seek(value);})// 时间显示Row() {Text(this.formatTime(this.currentPosition)).fontSize(12)Blank()Text(this.formatTime(this.duration)).fontSize(12)}.width('100%')// 控制按钮Row() {Button(this.isPlaying ? '暂停' : '播放').onClick(() => {if (this.isPlaying) {this.player.pause();} else {this.player.play();}})Button('停止').margin({ left: 20 }).onClick(() => {this.player.stop();})}.margin({ top: 15 })}}aboutToAppear() {this.startUpdatingPosition();}aboutToDisappear() {this.stopUpdatingPosition();}startUpdatingPosition() {this.updateInterval = setInterval(() => {// 更新当前播放位置}, 500);}stopUpdatingPosition() {if (this.updateInterval) {clearInterval(this.updateInterval);this.updateInterval = null;}}formatTime(seconds: number): string {const mins = Math.floor(seconds / 60);const secs = Math.floor(seconds % 60);return `${mins}:${secs.toString().padStart(2, '0')}`;}
}
5. 功能扩展建议
-
录音质量设置:
- 添加不同采样率和编码格式选项
- 实现高质量和低质量录音切换
-
录音编辑功能:
- 实现录音裁剪功能
- 添加淡入淡出效果
-
录音标签管理:
- 为录音添加标签和备注
- 实现录音分类管理
-
云同步功能:
- 集成华为云存储
- 实现录音多设备同步
-
高级音频处理:
- 添加降噪功能
- 实现音频变速播放
相关文章:
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...

SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...

多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...

select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...

SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...