目录

通过监听距离来实现音频播放设备在扬声器和听筒之间切换

问题现象

IM类软件聊天会话中发送或接收的语音类型消息(是短消息不是语音通话),在播放过程中通过监听距离传感器来切换听筒和扬声器播放。语音播放使用AVPlayer,如何实现;

背景知识

解决方案

  1. 设置管理全局音频输出设备的接口:
    let audioManager = audio.getAudioManager(); // 需要先创建AudioManager实例。
    let audioSessionManager = audioManager.getSessionManager(); // 再调用AudioManager的方法创建AudioSessionManager实例。
    let strategy: audio.AudioSessionStrategy = {
      concurrencyMode: audio.AudioConcurrencyMode.CONCURRENCY_PAUSE_OTHERS
    };
    
  2. 设置传感器接口,当设备贴近耳朵时,将全局播放设备切换为听筒,当设备远离时,全局播放设备切换为扬声器:
    sensor.on(sensor.SensorId.PROXIMITY, (response) => {
      if (response.distance === 0) {
        this.sessionIsSpeaker = false;
        audioSessionManager.setDefaultOutputDevice(audio.DeviceType.EARPIECE)
          .then(() => {
            console.info('AudioSession Succeeded in setting default EARPIECE device.');
          }).catch((err: BusinessError) => {
          console.error(`AudioSession setDefaultOutputDevice Fail: ${err}`);
        });
      } else if (response.distance === 5) {
        this.sessionIsSpeaker = true;
        audioSessionManager.setDefaultOutputDevice(audio.DeviceType.SPEAKER)
          .then(() => {
            console.info('AudioSession Succeeded in setting SPEAKER output device.');
          }).catch((err: BusinessError) => {
          console.error(`AudioSession setDefaultOutputDevice Fail: ${err}`);
        });
      }
    }, { interval: 1000000000 }); //采样周期1000000000ns,即1s

完整示例如下:

import { audio } from '@kit.AudioKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { sensor } from '@kit.SensorServiceKit';
import { common } from '@kit.AbilityKit';
import { media } from '@kit.MediaKit';


let audioManager = audio.getAudioManager(); // 需要先创建AudioManager实例。
let audioSessionManager = audioManager.getSessionManager(); // 再调用AudioManager的方法创建AudioSessionManager实例。
let strategy: audio.AudioSessionStrategy = {
  concurrencyMode: audio.AudioConcurrencyMode.CONCURRENCY_PAUSE_OTHERS
};




// 请用户切换为自己的播放逻辑,此处仅做模拟演示。
class AvPlayer {
  static avPlayer: media.AVPlayer | undefined;


  static async initAvplayer() {
    AvPlayer.avPlayer = await media.createAVPlayer();
    AvPlayer.avPlayer.on('stateChange', (state) => {
      if (!AvPlayer.avPlayer) {
        return;
      }
      switch (state) {
        case 'idle':
          console.info('触发了播放器idle状态');
          break;
        case 'initialized':
          AvPlayer.avPlayer.audioRendererInfo = {
            usage: audio.StreamUsage.STREAM_USAGE_VOICE_COMMUNICATION,
            rendererFlags: 0
          };
          console.info('触发了播放器initialized状态');
          AvPlayer.avPlayer.prepare();
          break;
        case 'prepared':
          console.info('触发了播放器prepared状态');
          AvPlayer.avPlayer.play();
          break;
        case 'completed':
          console.info('触发了播放器completed状态');
          break;
      }
    });
  }


  static async playMusic(url: media.AVFileDescriptor) {
    if (!AvPlayer.avPlayer) {
      return;
    }
    AvPlayer.avPlayer.fdSrc = url;
  }
}




@Entry
@ComponentV2
struct Index {
  @Local sessionIsSpeaker: boolean = true;


  aboutToAppear(): void {
    AvPlayer.initAvplayer();
    audioSessionManager.activateAudioSession(strategy);
    try {
      sensor.on(sensor.SensorId.PROXIMITY, (response) => {
        if (response.distance === 0) {
          this.sessionIsSpeaker = false;
          audioSessionManager.setDefaultOutputDevice(audio.DeviceType.EARPIECE)
            .then(() => {
              console.info('AudioSession Succeeded in setting default EARPIECE device.');
            }).catch((err: BusinessError) => {
            console.error(`AudioSession setDefaultOutputDevice Fail: ${err}`);
          });
        } else if (response.distance === 5) {
          this.sessionIsSpeaker = true;
          audioSessionManager.setDefaultOutputDevice(audio.DeviceType.SPEAKER)
            .then(() => {
              console.info('AudioSession Succeeded in setting SPEAKER output device.');
            }).catch((err: BusinessError) => {
            console.error(`AudioSession setDefaultOutputDevice Fail: ${err}`);
          });
        }
      }, { interval: 1000000000 }); //采样周期1000000000ns,即1s
    } catch (error) {
      let e: BusinessError = error as BusinessError;
      console.error(`Failed to invoke on. Code: ${e.code}, message: ${e.message}`);
    }
  }


  aboutToDisappear(): void {
    try {
      sensor.off(sensor.SensorId.PROXIMITY, () => {
        console.info('Succeeded in invoking off. Distance');
      });
    } catch (error) {
      let e: BusinessError = error as BusinessError;
      console.error(`Failed to invoke off. Code: ${e.code}, message: ${e.message}`);
    }
  }


  build() {
    Column({ space: 10 }) {
      Button('播放音乐')
        .onClick(async () => {
          let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
          // 请用户切换为自己的播放资源
          let fileDescriptor = await context.resourceManager.getRawFd('test.mp3');
          let avFileDescriptor: media.AVFileDescriptor =
            { fd: fileDescriptor.fd, offset: fileDescriptor.offset, length: fileDescriptor.length };
          AvPlayer.playMusic(avFileDescriptor);
        });
      Row() {
        Text(this.sessionIsSpeaker ? 'Speaker' : 'Ear');
      };
    }.width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center);
  }
}