HarmonyOS 鸿蒙Next中如何循环播放wav音频

HarmonyOS 鸿蒙Next中如何循环播放wav音频 如何点击按钮后,循环播放wav音频,5分钟自动关闭?

3 回复

使用 AVPlayer 去播放 wav 格式文件,它支持 m4a、aac、mp3、ogg、wav、flac、amr、ape。

具体如下,要么把 url 换成在线地址,或者在文件在沙箱目录下 demo 是文件在 rawfile 目录然后复制到沙箱在处理的

import { AudioPlayerUtil } from '../../utils/AudioPlayerUtil';
import common from '@ohos.app.ability.common';
import fs, { ListFileOptions, ReadOptions, ReadTextOptions, WriteOptions } from '@ohos.file.fs';

@Entry
@Component
struct AudioPlayerPage {
    private audioPlayerUtil: AudioPlayerUtil | null = null;
    private durationInput: string = '60';
    private context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    private SEP = '/'

    build() {
        Column({ space: 20 }) {
            Text('音频播放器')
                .fontSize(24)
                .fontWeight(FontWeight.Bold)
                .margin({ top: 40 });

            TextInput({ placeholder: '请输入自动关闭时长(秒)', text: this.durationInput })
                .type(InputType.Number)
                .width(280)
                .height(48)
                .fontSize(16)
                .onChange((value: string) => {
                    this.durationInput = value;
                });

            Button('rawfile复制本地')
                .width(280)
                .height(48)
                .backgroundColor('#007DFF')
                .fontColor('#FFFFFF')
                .fontSize(16)
                .onClick(() => {
                    let resourceManager = this.context.resourceManager;
                    let uintArray: Uint8Array = resourceManager.getRawFileContentSync("video.wav");
                    let pathDir = this.context.filesDir
                    console.error("pathDir:" + pathDir)
                    //创建目录
                    this.createOrExistsDir(pathDir)
                    let path = pathDir + "/video.wav"
                    this.createOrExistsFile(path)
                    if (fs.accessSync(path)) {
                        let isSuccess = this.writeEasy(path, uintArray.buffer, false)
                        if (isSuccess) {
                            console.error("写入成功")
                        } else {
                            console.error("写入失败")
                        }
                    }
                });

            Button('开始循环播放')
                .width(280)
                .height(48)
                .backgroundColor('#007DFF')
                .fontColor('#FFFFFF')
                .fontSize(16)
                .onClick(() => {
                    if (!this.audioPlayerUtil) {
                        this.audioPlayerUtil = new AudioPlayerUtil(this.context);
                    }
                    const duration = parseInt(this.durationInput) || 60;
                    let path = this.context.filesDir + "/video.wav"
                    if (!fs.accessSync(path)) {
                        console.error("文件不存在")
                        return
                    }
                    let file = this.openSync(path, fs.OpenMode.READ_WRITE);
                    this.audioPlayerUtil.startPlay(file, duration * 1000);
                });

            Button('停止播放')
                .width(280)
                .height(48)
                .backgroundColor('#FF6B6B')
                .fontColor('#FFFFFF')
                .fontSize(16)
                .onClick(() => {
                    this.audioPlayerUtil?.stopPlay();
                });
        }
        .width('100%')
        .alignItems(HorizontalAlign.Center)
        .padding(20);
    }

    createOrExistsDir(rootDir: string): void {
        // 确保目录路径以路径分隔符结尾
        rootDir = rootDir[rootDir.length - 1] !== this.SEP ? rootDir + this.SEP : rootDir;
        // 检查目录是否已存在
        if (!fs.accessSync(rootDir)) {
            // 如果不存在,则创建目录
            fs.mkdirSync(rootDir, true)
        }
    }

    createOrExistsFile(filePath: string): void {
        let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
        fs.closeSync(file)
    }

    writeEasy(path: string, content: ArrayBuffer | string, append: boolean = true): boolean {
        try {
            let file = this.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
            let offset = append ? fs.statSync(file.fd).size : 0
            let options: WriteOptions = { offset: offset, encoding: 'utf-8' };
            fs.writeSync(file.fd, content, options)
            fs.closeSync(file.fd); //关闭文件;
            return true
        } catch (err) {
            return false;
        }
    }

    openSync(path: string, mode: number = fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE): fs.File {
        return fs.openSync(path, mode);
    }
}
import { media } from '@kit.MediaKit';
import { BusinessError } from '@kit.BasicServicesKit';
import common from '@ohos.app.ability.common';
import fs from '@ohos.file.fs';

export class AudioPlayerUtil {
    private avPlayer: media.AVPlayer | null = null;
    private timerId: number = -1;
    private context: common.UIAbilityContext;

    constructor(context: common.UIAbilityContext) {
        this.context = context;
    }

    private async createAVPlayer(): Promise<media.AVPlayer> {
        return await media.createAVPlayer();
    }

    private setupPlayerListeners(): void {
        if (!this.avPlayer) return;

        this.avPlayer.on('stateChange', async (state: string) => {
            console.info(`AudioPlayer state changed: ${state}`);
            if (state === 'initialized') {
                this.avPlayer?.prepare();
            } else if (state === 'prepared') {
                this.avPlayer!.loop = true;
                this.avPlayer?.play();
            }
        });

        this.avPlayer.on('error', (error: BusinessError) => {
            console.error(`AudioPlayer error code: ${error.code}, message: ${error.message}`);
            this.release();
        });
    }

    async startPlay(path: fs.File, autoStopDurationMs: number = 300000): Promise<void> {
        this.stopPlay();
        try {
            this.avPlayer = await this.createAVPlayer();
            this.setupPlayerListeners();

            let avFile: media.AVFileDescriptor = {
                fd: path.fd
            }

            this.avPlayer.fdSrc = avFile;

            if (autoStopDurationMs > 0) {
                this.timerId = setTimeout(() => {
                    this.stopPlay();
                }, autoStopDurationMs);
            }
        } catch (error) {
            console.error(`Failed to start audio playback: ${error}`);
        }
    }

    stopPlay(): void {
        if (this.avPlayer) {
            try {
                this.avPlayer.stop();
                this.avPlayer.release();
            } catch (error) {
                console.error(`Error stopping player: ${error}`);
            }
            this.avPlayer = null;
        }
        if (this.timerId !== -1) {
            clearTimeout(this.timerId);
            this.timerId = -1;
        }
    }

    release(): void {
        this.stopPlay();
    }

    isPlaying(): boolean {
        return this.avPlayer !== null;
    }
}

更多关于HarmonyOS 鸿蒙Next中如何循环播放wav音频的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在鸿蒙Next中,使用 @ohos.multimedia.audio 模块的 createAudioPlayer() 创建播放器,设置 loop 属性为 true 即可循环播放wav音频。示例:audioPlayer.loop = true;。播放前需确保音频资源路径正确,并添加相应权限。

在HarmonyOS Next中实现点击按钮循环播放wav音频,并在5分钟后自动关闭,可以使用 AVPlayer 配合循环模式与延时任务来完成。示例代码如下:

import media from '@ohos.multimedia.media';
import common from '@ohos.app.ability.common';

// 创建AVPlayer实例
let avPlayer: media.AVPlayer = await media.createAVPlayer();
let context = getContext(this) as common.UIAbilityContext;
let fd = await context.resourceManager.getRawFileDescriptor('test.wav');

// 设置播放源
avPlayer.fdSrc = { fd: fd.fd, offset: fd.offset, length: fd.length };
// 设置为循环播放
avPlayer.loop = true;

// 播放
await avPlayer.play();

// 5分钟(300000毫秒)后停止并释放
setTimeout(() => {
  avPlayer.stop();
  avPlayer.release();
}, 300000);
  • 关键点
    • 循环播放:直接将 loop 属性设为 true,播放结束后会自动重新开始。
    • 资源加载:若 wav 文件在 rawfile 目录下,使用 getRawFileDescriptor 获取文件描述符,然后以 fdSrc 方式设置。
    • 自动关闭:利用 setTimeout 在指定时间后调用 stop() 停止播放,并调用 release() 释放资源。

若需要在按钮点击时触发播放,将上述代码放入按钮的 onClick 回调中即可。注意每次播放前最好先重置或释放旧的 AVPlayer 实例,避免资源冲突。

回到顶部