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

在鸿蒙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. 功能扩展建议

  1. ​录音质量设置​​:

    • 添加不同采样率和编码格式选项
    • 实现高质量和低质量录音切换
  2. ​录音编辑功能​​:

    • 实现录音裁剪功能
    • 添加淡入淡出效果
  3. ​录音标签管理​​:

    • 为录音添加标签和备注
    • 实现录音分类管理
  4. ​云同步功能​​:

    • 集成华为云存储
    • 实现录音多设备同步
  5. ​高级音频处理​​:

    • 添加降噪功能
    • 实现音频变速播放

相关文章:

  • Leetcode4(寻找两个正序数组的中位数)
  • Windows11 WSL2 Ubuntu编译安装perf工具
  • VSCode 没有添加Windows右键菜单
  • Java图形编程实战:从基础绘制到高级动画实现
  • 函数01 day10
  • 【PostgreSQL安装】保姆级安装教程+特性详解
  • 深入理解Go并发模型:从CSP理论到生产实践的完整指南
  • encodeURIComponent和decodeURIComponent
  • OpenHarmony按键分发流程(60%)
  • 安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
  • 云安全与网络安全:核心区别与协同作用解析
  • Android Jetpack Compose开发纯自定义表盘【可用于体重,温度计等项目】
  • 设置Outlook关闭时最小化
  • TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
  • Haption 力反馈遥操作机器人:6 自由度 + 低延迟响应,解锁精准远程操控体验
  • omi开源程序是AI 可穿戴设备的源码。戴上它,说话,转录,自动完成
  • USB Over IP专用硬件的5个特点
  • C/CPP 结构体、联合体、位段内存计算 指南
  • 从面试角度回答Android中ContentProvider启动原理
  • 网络六边形受到攻击
  • 网站建设公司推荐时代创信/整站优化包年
  • 济南网站建设泉诺/西安seo管理
  • 重庆网站推广免费软件/惠州seo关键字优化
  • 推广活动策划方案范文/深圳知名网络优化公司
  • 基于php网站开发/余姚网站seo运营
  • 哪个网站可以做视频软件/网址注册在哪里注册