功能介绍

在用户使用 “一起看”、“一起听” 等互动娱乐类应用时,如果需要在通话过程中播放音效或音乐文件等,而且让房间内的其他成员也听到声音,需要使用混音功能。混音功能可以将麦克风采集的音频数据与音频文件、PCM 音频数据等合并为一路音频流后,发布到房间内。本文将介绍如何使用混音功能。

功能变更日志
  1. 自客户端 SDK 3.32 起,支持对音乐文件的声道进行设置。
  2. 自客户端 SDK 3.36 起,全平台支持在线音频文件混音。
  3. 自客户端 SDK 3.38 起,支持调整音视频通话中使用的音频文件的播放速度。
  4. 自客户端 SDK 3.45.4 起,支持获取混音音频文件的实际播放时长,即歌曲不受停止、跳转、倍速、卡顿影响的播放时长。
功能使用

初始化引擎后,调用 getAudioMixingManager 获取 IAudioMixingManager 混音管理类(以安卓平台为例),了解相关混音功能。具体参见下表:

AndroidiOSmacOSWindowsWeb
IAudioMixingManagerByteRTCAudioMixingManagerByteRTCAudioMixingManagerIAudioMixingManagerIAudioMixingManager
播放本地音频文件

参考以下顺序,调用 API 实现此功能:

对同一个音频文件进行操作时,混音 ID 应保持一致

步骤调用目标接口
1(可选)预加载音乐文件preloadAudioMixing
2播放音乐文件startAudioMixing
3暂停播放pauseAudioMixing
4恢复播放resumeAudioMixing
5停止播放stopAudioMixing

除以上功能以外,RTC SDK 还支持调整音频播放音量和声道,设置音乐播放进度等功能。

播放 PCM 音频数据

参考以下顺序,调用 API 实现此功能:

步骤调用目标Android 接口
1启动 PCM 音频数据混音enableAudioMixingFrame
2周期性推送 PCM 音频数据,以开始播放pushAudioMixingFrame
3结束 PCM 音频数据混音disableAudioMixingFrame

示例代码以 objective-C 为例,介绍播放 PCM 音频的相关功能。

启动混音
	- (void)startAudioMixing {
	    ByteRTCAudioMixingManager *manager = [self.rtcKit getAudioMixingManager];
	    [manager enableAudioMixingFrame:kAudioMixingID type:ByteRTCAudioMixingTypePlayout];
	}
objectivec
播放混音

以下示例代码以 ttplayer 的数据为例,说明 SDK 要求的数据处理方式。

	/// 处理 PCM 数据
	/// @param inouts float 类型 PCM 数据
	/// @param samples 采样点数
	- (void)processAudio:(float **)inouts samples:(int)samples {
	    
	    // 声道数。如果声道数超过 2 个,需要先将音频进行减声道处理后再传给 SDK。
	    int channelsCount = _channels;
	    // 采样率
	    int samplerate = _samplerate;
	    
	    int length = samples * channelsCount;
	    if (self.length < length) {
	        if (self.length > 0) {
	            self.length = 0;
	            delete [] (buffer);
	        }
	        // buffer 成员变量
	        buffer = new int16_t[length*2];
	        self.length = length*2;
	    }
	// 如果数据为非交织格式,需要转换为交织格式
	    for (int i = 0; i < channelsCount; i++) {
	        int offset = i;
	        float *dataList = inouts[i];
	        for (int j = 0; j < samples; j++) {
	            float data = dataList[j];
	// 转换为 int16 类型
	            int value = data * INT16_MAX;
	            if(value > INT16_MAX){
	                value = INT16_MAX;
	            } else if(value < INT16_MIN){
	                value = INT16_MIN;
	            }
	            buffer[offset] = value;
	            offset += _channels;
	        }
	    }
	    
	    ByteRTCAudioFrame *frame = [[ByteRTCAudioFrame alloc] init];
	    frame.buffer = [NSData dataWithBytes:buffer length:length*2]; // 用类方法,不参与 buffer 生命周期管理
	    
	    frame.samples = samples;
	    frame.channel = (ByteRTCAudioChannel)channelsCount;
	    frame.sampleRate = (ByteRTCAudioSampleRate)samplerate;
	    
	    ByteRTCAudioMixingManager *manager = [self.rtcKit getAudioMixingManager];
	    [manager pushAudioMixingFrame:kAudioMixingID audioFrame:frame];
	}
objectivec
调整混音音量
	- (void)setAudioMixingVolume:(CGFloat)audioMixingVolume {
	    ByteRTCAudioMixingManager *audioMixingManager = [self.rtcKit getAudioMixingManager];
	    [audioMixingManager setAudioMixingVolume:kAudioMixingID volume:volume type:ByteRTCAudioMixingTypePlayout];
	}
objectivec
结束混音
	- (void)stopAudioMixing {
	    ByteRTCAudioMixingManager *manager = [self.rtcKit getAudioMixingManager];
	    [manager disableAudioMixingFrame:kAudioMixingID];
	}
objectivec