讨论广场 问答详情
在 Harmony 开发中使用 AudioRenderer 播放 PCM 数据时只“滴”一下就没声音,播放流程应该怎么写?
李游Leo 2025-11-17 09:53:50
62 评论 分享
harmonyos

使用 AudioKit 的 createAudioRenderer 播放一段本地 PCM 数据(S16LE,48kHz,双声道)。创建 AudioRenderer 成功,日志也打印了 start success,但实际效果是只听到很短的一点点声音或者完全没声音,好像只播放了几帧数据就结束了。

 

下面我代码:

import { audio } from '@kit.AudioKit';
import fileIo from '@ohos.file.fs';

let audioRenderer: audio.AudioRenderer | undefined;

async function playPcm(filePath: string) {
  const options: audio.AudioRendererOptions = {
    streamInfo: {
      samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000,
      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
    }
  };

  audioRenderer = await audio.createAudioRenderer(options);
  await audioRenderer.start();

  // 只读取一次数据就 stop
  const fd = fileIo.openSync(filePath, fileIo.OpenMode.READ_ONLY);
  const buffer = new ArrayBuffer(4096);
  fileIo.readSync(fd, buffer);
  await audioRenderer.write(buffer);
  await audioRenderer.stop();
  await audioRenderer.release();
  fileIo.closeSync(fd);
}

没有明显的报错,只是播放效果不对。

 

62 评论 分享
写回答
全部评论(1)

现象是只播放一次缓冲就立刻 stop,本质问题是:

  1. 只调用了一次 write(),数据量太少;

  2. 播放结束前没有等渲染器把 buffer 播完就 stop / release

一个更符合 AudioKit 推荐流程的写法是:

import { audio } from '@kit.AudioKit';
import { BusinessError } from '@kit.BasicServicesKit';
import fileIo from '@ohos.file.fs';

async function playPcmLoop(filePath: string) {
  const options: audio.AudioRendererOptions = {
    streamInfo: {
      samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000,
      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
    }
  };

  let renderer: audio.AudioRenderer;
  try {
    renderer = await audio.createAudioRenderer(options);
  } catch (err) {
    const be = err as BusinessError;
    console.error(`createAudioRenderer error: code=${be.code}, msg=${be.message}`);
    return;
  }

  const fd = fileIo.openSync(filePath, fileIo.OpenMode.READ_ONLY);
  const bufferSize = 4096;
  const buffer = new ArrayBuffer(bufferSize);

  await renderer.start();

  while (true) {
    const readLen = fileIo.readSync(fd, buffer);
    if (readLen <= 0) {
      break; // 文件读完
    }
    const realData = buffer.slice(0, readLen);
    await renderer.write(realData);
  }

  // 等待内部缓冲播放完(不同版本可能有 drain()/flush(),按当前 SDK 文档为准)
  await renderer.stop();
  await renderer.release();
  fileIo.closeSync(fd);
}

关键点:

  • 播放必须循环调用 write,直到数据写完

  • 不要在第一帧写完就立刻 stop,要等播放逻辑结束后再 stop + release

  • 若当前 SDK 提供 drain() 或类似接口,可以在 stop 之前调用让渲染器把内部缓冲播放完。

按上述流程调整后,就不会只“滴”一下了,而是完整播放整个 PCM 文件。

2025-11-17 10:42:20