目录

前置条件

参考文档:

开发步骤:

1.配置音频渲染参数AudioRendererOption并创建AudioRenderer实例:

2.音频渲染开发

 3.总结


前置

API版本:OpenHarmony SDK 11(full sdk)

IDE版本:DevEco Studio 4.1 Release

参考文档:

openHarmony AudioRenderer开发音频播放功能https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/media/audio/using-audiorenderer-for-playback.md
@ohos.multimedia.audio (音频管理)https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/reference/apis-audio-kit/js-apis-audio.md

开发步骤:

1.配置音频渲染参数AudioRendererOption并创建AudioRenderer实例:

const TAG = "Audio: "  
private readonly audioRendererOptions: audio.AudioRendererOptions[] = [
    {
      streamInfo: {
        samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100, // 采样率
        channels: audio.AudioChannel.CHANNEL_2, // 通道
        sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式
        encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式
      },
      rendererInfo: {
        usage: audio.StreamUsage.STREAM_USAGE_MUSIC, // 音频流使用类型
        rendererFlags: 0 // 音频渲染器标志
      }
    },
    {
      streamInfo: {
        samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100,
        channels: audio.AudioChannel.CHANNEL_2,
        sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
        encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
      },
      rendererInfo: {
        usage: audio.StreamUsage.STREAM_USAGE_RINGTONE,
        rendererFlags: 0
      }
    }
  ];
 private readonly audioSources = ['ring.wav', 'ring.wav'];
  // 音频管理属性
  audioRenderers: audio.AudioRenderer[] = []; 
  private fileDescriptors: resourceManager.RawFileDescriptor[] = [];//音频资源数组

设置好渲染参数之后这里我们定义一个初始化铃声ringInit()方法:

  async ringInit(): Promise<void> {
    //设置铃声
    await this.getStageFileDescriptor(this.audioSources[0]);
    await this.getStageFileDescriptor(this.audioSources[1]);
    try {
      this.audioRenderers[1] = await audio.createAudioRenderer(this.audioRendererOptions[1]);
      await this.audioRenderers[1].setInterruptMode(audio.InterruptMode.INDEPENDENT_MODE); 
      this.listenState(1);
    } catch (err) {
      let error = err as BusinessError;
      Logger.error(TAG, `audioRenderer create ,Error: ${JSON.stringify(error)}`);
      return;
    }
  }

其中getStageFileDescriptor方法是为了从rawfile目录获取资源文件:

  async getStageFileDescriptor(fileName: string): Promise<void> {
    this.appContext = getContext(this)
    if (this.appContext) {
      Logger.error(TAG + "appContext successful")
      let mgr = this.appContext.resourceManager;
      await mgr.getRawFd(fileName).then(value => {
        this.fileDescriptors.push(value) //成功后push到数组中
        Logger.info(TAG, 'case getRawFileDescriptor success fileName: ' + fileName)
      }).catch((error: BusinessError) => {
        Logger.info(TAG, 'case getRawFileDescriptor err: ' + error)
      });
    } else {
      Logger.error(TAG + "appContext undefined")
    }
  }

而listenState方法是设置监听事件:

  private listenState(index: number): void {
    this.audioRenderers[index].on('stateChange', (state) => {
      Logger.info(TAG, `Player ${index} state changed to: ${state}`);
    });
  }

一切准备就绪之后就可以开启渲染功能的开发了。

2.音频渲染开发

  async ringPlay(): Promise<void> {
    if (this.audioRenderers[1] === null) {
      Logger.error(TAG, "audioRenderers null")
      return
    }
    let bufferSize: number = 0;
    try {
      bufferSize = await this.audioRenderers[1].getBufferSize();
      await this.audioRenderers[1].start();
      Logger.info(TAG, "audioRenderers successful")
    }
    catch (err) {
      let error = err as BusinessError;
      Logger.error(TAG + `audioRenderer start : Error: ${JSON.stringify(error)}`);
      return;
    }
    try {
      let buf = new ArrayBuffer(bufferSize);
      let start = this.fileDescriptors[1].offset;
      let cur = start;
      // 添加数据循环写入
      while (cur < start + this.fileDescriptors[1].length) {
        let options: Options = { offset: cur, length: bufferSize };
        await fs.read(this.fileDescriptors[1].fd, buf, options);
        await this.audioRenderers[1].write(buf);
        cur += bufferSize;
      }
      Logger.error(TAG + AudioManager.Audio.audioRenderers[1].state)

    } catch (err) {
      Logger.error(TAG, `Write data failed: ${err}`);
    }

  }

代码分析:其中的输出日志封装Logger可参考GitCode - 全球开发者的开源社区,开源代码托管平台GitCode是面向全球开发者的开源社区,包括原创博客,开源代码托管,代码协作,项目管理等。与开发者社区互动,提升您的研发效率和质量。https://gitcode.com/openharmony-sig/ohos_axios/blob/master/library/src/main/ets/components/lib/LogUtil.js

 下面为暂停代码:

  async ringStop(): Promise<void> {
    try {
      await this.audioRenderers[1].stop();
      Logger.info(TAG, 'stop successful')
    }
    catch (err) {
      let error = err as BusinessError;
      Logger.error(TAG, `render stop err:${JSON.stringify(error)}`);
    }
  }

需要注意的是音频管理的状态应遵循AudioRenderer状态变化示意图:

 3.总结

后续会继续分享音频音量管理的功能。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐