怎么在播放音频时不打断其他APP音频
==================

#### 问题现象

播放音频时，会暂停后台音乐播放，如何降低其他APP音量或者与其他音频同时播放？

#### 背景知识

*   系统预设了默认的[音频焦点](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/audio-playback-concurrency#音频焦点)策略，根据音频流的类型及启动的先后顺序，对所有播放和录制音频流进行统一管理。
*   应用可利用[音频会话管理（AudioSessionManager）](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/audio-session-management)提供的接口，通过AudioSession主动管理应用内音频流的焦点，自定义本应用音频流的焦点策略。

#### 解决方案

*   **方案一**：
    
    应用可通过配置合适的音频流类型[StreamUsage](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-audio-e#streamusage)，根据系统默认的音频焦点策略，达到降低音量或同时播放的效果。
    
    例如先播放STREAM\_USAGE\_MUSIC音乐音频，后播STREAM\_USAGE\_NAVIGATION导航音频可实现降低先播音频音量效果，详情可参考[系统默认焦点策略表](https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-audio-focus-management#section17923135513547)。
    
*   **方案二**：
    
    当系统默认焦点策略不满足应用焦点需求时，可通过[音频会话（AudioSession）](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/audio-playback-concurrency#音频会话audiosession)自定义本应用的焦点策略。
    
    系统预设四种并发模式可按需选择：
    
    *   默认模式（CONCURRENCY\_DEFAULT）：即系统默认的音频焦点策略。
    *   并发模式（CONCURRENCY\_MIX\_WITH\_OTHERS）：和其他音频流并发。
    *   降低音量模式（CONCURRENCY\_DUCK\_OTHERS）：和其他音频流并发，并且降低其他音频流的音量。
    *   暂停模式（CONCURRENCY\_PAUSE\_OTHERS）：暂停其他音频流，待释放焦点后通知其他音频流恢复。
    
    详细使用步骤可参考官网[使用AudioSession管理应用音频焦点(ArkTS)](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/audio-session-management)或者[使用AudioSession管理应用音频焦点(C/C++)](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/using-ohaudio-for-session)。
    
    import { audio } from '@kit.AudioKit';
    import { common } from '@kit.AbilityKit';
    import { BusinessError } from '@kit.BasicServicesKit';
    
    let audioRenderer: audio.AudioRenderer;
    let audioStreamInfo: audio.AudioStreamInfo = {
      samplingRate: audio.AudioSamplingRate.SAMPLE\_RATE\_8000, _// 采样率。_
      channels: audio.AudioChannel.CHANNEL\_1, _// 通道。_
      sampleFormat: audio.AudioSampleFormat.SAMPLE\_FORMAT\_S16LE, _// 采样格式。_
      encodingType: audio.AudioEncodingType.ENCODING\_TYPE\_RAW _// 编码格式。_
    };
    let audioRendererInfo: audio.AudioRendererInfo = {
      usage: audio.StreamUsage.STREAM\_USAGE\_MUSIC, _// 音频流使用类型：音乐。根据业务场景配置，参考StreamUsage。_
      rendererFlags: 0 _// 音频渲染器标志。_
    };
    let audioRendererOptions: audio.AudioRendererOptions = {
      streamInfo: audioStreamInfo,
      rendererInfo: audioRendererInfo
    };
    
    let audioManager = audio.getAudioManager();
    let audioSessionManager: audio.AudioSessionManager = audioManager.getSessionManager();
    let strategy: audio.AudioSessionStrategy = {
      concurrencyMode: audio.AudioConcurrencyMode.CONCURRENCY\_DEFAULT
    };
    
    @Entry
    @Component
    export struct AudioSessionDemo {
      context = this.getUIContext().getHostContext() as common.UIAbilityContext;
      audioData: Uint8Array = generateTestPCM(); _// 测试PCM数据，按需替换为其他音频数据源_
      writeOffset = 0;
    
      async aboutToAppear(): Promise<void> {
        audioRenderer = await audio.createAudioRenderer(audioRendererOptions);
        await this.init(); _//初始化_
      }
    
      async aboutToDisappear(): Promise<void> {
        try {
          await audioSessionManager.deactivateAudioSession();
        } catch (error) {
          console.error(\`deactivateAudioSession ${JSON.stringify(error)}\`);
        }
        await audioRenderer.release();
      }
    
      build() {
        Column({ space: 10 }) {
          Select(\[{ value: '默认模式（CONCURRENCY\_DEFAULT）' },
            { value: '并发模式（CONCURRENCY\_MIX\_WITH\_OTHERS）' },
            { value: '降低音量模式（CONCURRENCY\_DUCK\_OTHERS）' },
            { value: '暂停模式（CONCURRENCY\_PAUSE\_OTHERS）' }\])
            .value('默认模式（CONCURRENCY\_DEFAULT）')
            .optionFont({ size: 12 })
            .onSelect((index: number, text: string) => {
              console.info('Select:' + index + text);
              strategy = {
                concurrencyMode: text === '并发模式（CONCURRENCY\_MIX\_WITH\_OTHERS）' ?
                  audio.AudioConcurrencyMode.CONCURRENCY\_MIX\_WITH\_OTHERS :
                  text === '降低音量模式（CONCURRENCY\_DUCK\_OTHERS）' ? audio.AudioConcurrencyMode.CONCURRENCY\_DUCK\_OTHERS :
                    text === '暂停模式（CONCURRENCY\_PAUSE\_OTHERS）' ? audio.AudioConcurrencyMode.CONCURRENCY\_PAUSE\_OTHERS :
                      audio.AudioConcurrencyMode.CONCURRENCY\_DEFAULT
              };
            });
    
          Button('激活AudioSession并播放')
            .onClick(async () => {
              console.info(\`strategy: ${JSON.stringify(strategy)}}\`);
              try {
                await audioSessionManager.activateAudioSession(strategy);
                await this.stopAndFlush();
                await audioRenderer.start();
              } catch (error) {
                console.error(\`activateAudioSession ${JSON.stringify(error)}\`);
              }
            });
    
          Button('取消激活AudioSession并停止播放').onClick(async () => {
            try {
              await audioSessionManager.deactivateAudioSession();
              await this.stopAndFlush();
            } catch (error) {
              console.error(\`deactivateAudioSession ${JSON.stringify(error)}\`);
            }
          });
        }
        .padding(20)
        .justifyContent(FlexAlign.Center)
        .width('100%')
        .height('100%');
      }
    
      async init() {
        audioRenderer.on('writeData', (buffer: ArrayBuffer) => {
          if (!this.audioData) {
            return audio.AudioDataCallbackResult.INVALID;
          }
          let bufferView = new Uint8Array(buffer);
          let writeLen = Math.min(buffer.byteLength, this.audioData.byteLength - this.writeOffset);
          if (writeLen <= 0) {
            this.writeOffset = 0; _//归零循环播放，测试用_
            console.info('Play Done');
            return audio.AudioDataCallbackResult.INVALID;
          }
          bufferView.set(this.audioData.slice(this.writeOffset, this.writeOffset + writeLen));
          this.writeOffset += writeLen;
          return audio.AudioDataCallbackResult.VALID;
        });
      }
    
      async stopAndFlush() {
        console.info('renderer status' + audioRenderer.state);
        audioRenderer.stop().then(() => {
          console.error('Renderer stop ok.');
        }).catch((err: BusinessError) => {
          console.error('Renderer stop failed. ', err);
        });
        audioRenderer.flush().then(() => {
          console.error('Renderer flush ok.');
        }).catch((err: BusinessError) => {
          console.error('renderer flush err. ' + err);
        });
        this.writeOffset = 0;
      }
    }
    
    function generateTestPCM(): Uint8Array {
      const sampleRate = 8000;
      const noteDuration = 0.5;
      const amplitude = 0.35;
    
      const freqMap: Record<number, number> = {
        1: 523.25, // C5
        2: 587.33, // D5
        3: 659.25, // E5
        4: 698.46, // F5
        5: 783.99, // G5
        6: 880.00, // A5
      };
    
      const melody = \[
        1, 1, 5, 5, 6, 6, 5, 0,
        4, 4, 3, 3, 2, 2, 1, 0,
        5, 5, 4, 4, 3, 3, 2, 0
      \];
    
      const samplesPerNote = Math.floor(sampleRate \* noteDuration); _// 4000_
      const totalSamples = samplesPerNote \* melody.length; _// 96,000_
      const buffer = new ArrayBuffer(totalSamples \* 2); _// 192,000 bytes_
      const view = new DataView(buffer);
    
      let idx = 0;
      for (const note of melody) {
        const freq = note ? freqMap\[note\] : 0;
        for (let i = 0; i < samplesPerNote; i++) {
          const t = i / sampleRate;
          const wave = freq ? amplitude \* Math.sin(2 \* Math.PI \* freq \* t) : 0;
          const sample = Math.round(wave \* 32767);
          const clamped = Math.max(-32768, Math.min(32767, sample));
          view.setInt16(idx \* 2, clamped, true);
          idx++;
        }
      }
      return new Uint8Array(buffer);
    }