
鸿蒙5 AI语音备忘录开发实战:语音转写与多设备同步 原创
鸿蒙5 AI语音备忘录开发实战:语音转写与多设备同步
一、项目概述与架构设计
本AI语音备忘录应用基于鸿蒙5的AI能力与分布式技术实现,主要功能包括:
语音实时转文字(ASR)
备忘录内容跨设备同步
语音指令控制
多设备协同编辑
技术架构图
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
手机设备 │ │ 平板设备 │ │ 手表设备 │
┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │
│ 语音输入 │─┼───▶│ │ 文本显示 │ │ │ │ 快捷查看 │ │
└────────┘ │ │ └────────┘ │ │ └────────┘ │
└───────┬─────┘ └───────┬─────┘ └───────┬─────┘
│ │
└─────────┬────────┴─────────┬────────┘
│
┌───────▼───────┐ ┌───────▼───────┐
分布式数据服务 │ │ AI语音服务 │
└───────────────┘ └───────────────┘
二、核心代码实现
语音识别服务封装
// VoiceService.ets
import asr from ‘@ohos.ai.asr’;
import distributedData from ‘@ohos.data.distributedData’;
export class VoiceService {
private asrEngine: asr.AsrEngine;
private kvStore: distributedData.KVStore;
private readonly STORE_ID = ‘voice_memo_store’;
async init() {
// 初始化ASR引擎
const config: asr.AsrConfig = {
language: ‘zh-CN’,
feature: asr.AsrFeature.FEATURE_WORD_FLUX
};
this.asrEngine = await asr.createAsrEngine();
await this.asrEngine.init(config);
// 初始化分布式数据
const kvManager = await distributedData.createKVManager({
bundleName: 'com.example.voicememo'
});
this.kvStore = await kvManager.getKVStore(this.STORE_ID, {
createIfMissing: true,
autoSync: true
});
async startRecording(callback: (text: string) => void) {
// 开始录音并实时转写
await this.asrEngine.on('result', (result: asr.AsrResult) => {
if (result.text) {
callback(result.text);
this.syncTranscript(result.text);
});
await this.asrEngine.start();
async stopRecording() {
await this.asrEngine.stop();
private async syncTranscript(text: string) {
try {
const memo = {
text,
timestamp: new Date().getTime(),
deviceId: deviceInfo.deviceId
};
await this.kvStore.put('current_memo', JSON.stringify(memo));
catch (err) {
console.error('Sync transcript failed:', err);
}
subscribeTranscriptChanges(callback: (text: string) => void) {
this.kvStore.on(‘dataChange’, distributedData.SubscribeType.SUBSCRIBE_TYPE_ALL, (changes) => {
changes.forEach(change => {
if (change.key === ‘current_memo’) {
const memo = JSON.parse(change.value);
if (memo.deviceId !== deviceInfo.deviceId) {
callback(memo.text);
}
});
});
}
备忘录主界面实现
// MemoPage.ets
@Entry
@Component
struct MemoPage {
@State currentText: string = ‘’;
@State memoList: Array<MemoItem> = [];
@State isRecording: boolean = false;
@State connectedDevices: Array<DeviceInfo> = [];
private voiceService: VoiceService = new VoiceService();
private deviceManager: DeviceManager = new DeviceManager();
aboutToAppear() {
this.voiceService.init();
this.voiceService.subscribeTranscriptChanges(this.handleTranscriptChange.bind(this));
this.deviceManager.startDiscovery();
build() {
Column() {
// 设备连接状态
DeviceStatusBar({ devices: this.connectedDevices })
// 语音输入区域
VoiceInputPanel({
isRecording: this.isRecording,
onStart: this.startRecording.bind(this),
onStop: this.stopRecording.bind(this)
})
// 实时转写文本
Scroll() {
Text(this.currentText)
.fontSize(16)
.padding(10)
.height(‘40%’)
// 历史备忘录列表
MemoList({
items: this.memoList,
onSelect: this.selectMemo.bind(this)
})
}
private async startRecording() {
this.isRecording = true;
await this.voiceService.startRecording((text) => {
this.currentText = text;
});
private async stopRecording() {
this.isRecording = false;
await this.voiceService.stopRecording();
this.saveMemo();
private handleTranscriptChange(text: string) {
this.currentText = text;
private saveMemo() {
if (this.currentText.trim()) {
this.memoList = [...this.memoList, {
id: Date.now().toString(),
text: this.currentText,
timestamp: new Date()
}];
this.currentText = '';
}
// 其他方法…
多设备同步控制
// DeviceManager.ets
import deviceManager from ‘@ohos.distributedHardware.deviceManager’;
export class DeviceManager {
private deviceList: Array<DeviceInfo> = [];
async startDiscovery() {
try {
const manager = await deviceManager.createDeviceManager(‘com.example.voicememo’);
manager.on('deviceStateChange', (data) => {
this.handleDeviceChange(data);
});
manager.startDeviceDiscovery(['voice_memo_sync']);
catch (err) {
console.error('Device discovery failed:', err);
}
private handleDeviceChange(data: deviceManager.DeviceStateChangeData) {
if (data.action === 0) { // 设备上线
if (!this.deviceList.some(d => d.deviceId === data.device.deviceId)) {
this.deviceList = […this.deviceList, data.device];
AppStorage.setOrCreate(‘connectedDevices’, this.deviceList);
} else if (data.action === 1) { // 设备下线
this.deviceList = this.deviceList.filter(d => d.deviceId !== data.device.deviceId);
AppStorage.setOrCreate('connectedDevices', this.deviceList);
}
async syncToDevice(deviceId: string, memo: MemoItem) {
try {
const kvManager = await distributedData.createKVManager({
bundleName: ‘com.example.voicememo’
});
const kvStore = await kvManager.getKVStore(‘memo_sync_store’);
await kvStore.put(memo_${memo.id}, JSON.stringify({
...memo,
sourceDevice: deviceInfo.deviceId,
targetDevice: deviceId
}));
catch (err) {
console.error('Sync memo failed:', err);
}
三、关键技术创新点
实时语音转写优化
// 语音识别参数优化
const optimizedConfig: asr.AsrConfig = {
language: ‘zh-CN’,
feature: asr.AsrFeature.FEATURE_WORD_FLUX,
audioFormat: {
sampleRate: 16000,
channelCount: 1,
encoding: asr.AudioEncoding.ENCODING_PCM_16BIT
},
vad: {
enable: true,
frontWaitTime: 300,
backWaitTime: 500
};
// 语音端点检测
this.asrEngine.on(‘vadChange’, (state: asr.VadState) => {
if (state === asr.VadState.VAD_STATE_VOICE_END) {
this.saveCurrentMemo();
});
多设备冲突解决策略
// 冲突解决算法
private resolveMemoConflict(local: MemoItem, remote: MemoItem): MemoItem {
// 策略1: 时间戳优先
if (remote.timestamp > local.timestamp) {
return remote;
// 策略2: 内容长度优先
if (remote.text.length > local.text.length) {
return remote;
return local;
// 在数据变更监听中应用
this.kvStore.on(‘dataChange’, (changes) => {
changes.forEach(change => {
if (change.key.startsWith(‘memo_’)) {
const remoteMemo = JSON.parse(change.value);
const localMemo = this.findMemo(remoteMemo.id);
if (localMemo) {
const resolved = this.resolveMemoConflict(localMemo, remoteMemo);
this.updateMemo(resolved);
else {
this.addMemo(remoteMemo);
}
});
});
语音指令识别
// 语音命令处理
private processVoiceCommand(text: string) {
const commands = [
pattern: /保存备忘录/, action: this.saveMemo.bind(this) },
pattern: /清空内容/, action: this.clearText.bind(this) },
pattern: /同步到平板/, action: this.syncToTablet.bind(this) }
];
for (const cmd of commands) {
if (cmd.pattern.test(text)) {
cmd.action();
break;
}
// 在录音回调中添加
this.voiceService.startRecording((text) => {
this.currentText = text;
this.processVoiceCommand(text);
});
四、性能优化方案
音频处理优化
// 音频缓冲设置
const audioOptions = {
bufferSize: 8192, // 8KB缓冲区
sampleRate: 16000,
channels: 1
};
// 在录音前配置
audio.createAudioCapturer(audioOptions, (err, capturer) => {
if (!err) {
this.audioCapturer = capturer;
});
网络自适应同步
// 根据网络状况调整同步策略
network.getDefaultNet().on(‘netCapabilitiesChange’, (capabilities) => {
const isWifi = capabilities.bearerTypes.includes(network.NetBearType.BEARER_WIFI);
this.kvStore.setSyncParam(‘current_memo’, isWifi); // 仅在WiFi下自动同步
this.syncInterval = isWifi ? 1000 : 5000; // 调整同步间隔
});
设备资源适配
// 根据设备类型调整功能
private setupDeviceSpecificFeatures() {
switch(deviceInfo.deviceType) {
case ‘phone’:
this.enableVoiceInput(true);
break;
case ‘watch’:
this.enableVoiceInput(false);
this.loadCompactUI();
break;
default:
this.enableFullFeatures();
}
五、完整示例代码
语音输入面板组件
// VoiceInputPanel.ets
@Component
struct VoiceInputPanel {
@Prop isRecording: boolean;
@State voiceLevel: number = 0;
build() {
Column() {
if (this.isRecording) {
// 录音中状态
Row() {
Image(‘mic_on.png’)
.width(40)
.height(40)
// 音量波动动画
ForEach(Array.from({length: 5}), (_, i) => {
Rect()
.width(4)
.height(10 + this.voiceLevel * i)
.fill(Color.Blue)
.margin(2)
})
.animation({ duration: 200, iterations: -1 })
Button('停止录音')
.onClick(() => this.onStop())
else {
// 待机状态
Button('开始录音', { type: ButtonType.Capsule })
.onClick(() => this.onStart())
}
.padding(20)
private updateVoiceLevel() {
setInterval(() => {
if (this.isRecording) {
this.voiceLevel = Math.random() * 10;
}, 100);
}
备忘录列表组件
// MemoList.ets
@Component
struct MemoList {
@Prop items: Array<MemoItem>;
@State selectedId: string = ‘’;
build() {
List() {
ForEach(this.items, (item) => {
ListItem() {
MemoListItem({
memo: item,
isSelected: this.selectedId === item.id
})
.onClick(() => {
this.selectedId = item.id;
this.onSelect(item);
})
})
.divider({ strokeWidth: 1, color: ‘#F1F1F1’ })
}
@Component
struct MemoListItem {
@Prop memo: MemoItem;
@Prop isSelected: boolean;
build() {
Row() {
Column() {
Text(this.memo.text)
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(this.formatDate(this.memo.timestamp))
.fontSize(12)
.opacity(0.6)
.layoutWeight(1)
if (this.isSelected) {
Image('checked.png')
.width(20)
.height(20)
}
.padding(10)
.backgroundColor(this.isSelected ? '#E6F7FF' : Color.White)
}
六、项目部署与测试
权限配置
在module.json5中添加:
"requestPermissions": [
"name": "ohos.permission.MICROPHONE"
},
"name": "ohos
