功能介绍

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

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

RTC SDK 提供了音效混音类和音乐混音类。
一般来说,对于短时间的音效(比如小于 20s),可以使用音效类;对于较长的音频或 PCM 数据,可以使用音乐类。

类和初始化

参见下表:

AndroidiOSmacOSWindowsWeb
初始化音效管理类getAudioEffectPlayergetAudioEffectPlayergetAudioEffectPlayergetAudioEffectPlayergetAudioEffectPlayer
音效混音管理类IAudioEffectPlayerByteRTCAudioEffectPlayerByteRTCAudioEffectPlayerIAudioEffectPlayerIAudioEffectPlayer
初始化音乐管理类getMediaPlayergetMediaPlayergetMediaPlayergetMediaPlayergetMediaPlayer
音乐混音管理类IMediaPlayerByteRTCMediaPlayerByteRTCMediaPlayerIMediaPlayerIMediaPlayer
播放本地音频/音效文件

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

以下接口在音效管理类和音乐管理类中都有。
对同一个音频文件进行操作时,混音 ID 应保持一致

步骤调用目标接口
1设置回调句柄setEventHandler
2(可选)预加载音乐文件preload
3播放音乐文件start
4暂停播放pause
5恢复播放resume
6停止播放stop

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

播放 PCM 音频数据

此功能仅在音乐管理类中实现。

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

步骤调用目标Android 接口
1设置回调句柄setEventHandler
2启动 PCM 音频数据混音openWithCustomSource
3周期性推送 PCM 音频数据,以开始播放pushExternalAudioFrame
4结束 PCM 音频数据混音stop
示例代码

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

启动混音
	- (void)startAudioMixing {
		// playerId must be in range of [0,3]
	    ByteRTCMediaPlayer *mediaPlayer = [self.rtcKit getMediaPlayer:playerId];
		ByteRTCMediaPlayerCustomSource* source = [[ByteRTCMediaPlayerCustomSource alloc] init];
		source.mode = ByteRTCMediaPlayerCustomSourceModePush;
		source.type = ByteRTCMediaPlayerCustomSourceStreamTypeRaw;
		source.provider = NULL;
		ByteRTCMediaPlayerConfig *config = [[ByteRTCMediaPlayerConfig alloc] init];
		config.type = ByteRTCAudioMixingTypePlayout;;
	    int ret = [mediaPlayer openWithCustomSource:source config:config];
	}
Objective-C
播放混音

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

	/// Deal with PCM data
	/// @param inouts PCM data in float
	/// @param samples number of samples
	- (void)processAudio:(float **)inouts samples:(int)samples {
	    
	    // Number of channels. If the number is more than 2, cut it down to 2.
	    int channelsCount = _channels;
	    int samplerate = _samplerate;
	    
	    int length = samples * channelsCount;
	    if (self.length < length) {
	        if (self.length > 0) {
	            self.length = 0;
	            delete [] (buffer);
	        }
	        buffer = new int16_t[length*2];
	        self.length = length*2;
	    }
	// If the data is in a non-interleaved format, convert it to an interleaved format.
	    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];
	// convert to 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]; // Using class methods that do not participate in buffer lifecycle management.
	    
	    frame.samples = samples;
	    frame.channel = (ByteRTCAudioChannel)channelsCount;
	    frame.sampleRate = (ByteRTCAudioSampleRate)samplerate;
	    
	    ByteRTCMediaPlayer *mediaPlayer = [self.rtcKit getMediaPlayer:playerId];
	    [mediaPlayer pushExternalAudioFrame:frame];
	}
Objective-C
调整混音音量
	- (void)setAudioMixingVolume:(int)Volume {
	    ByteRTCMediaPlayer *mediaPlayer = [self.rtcKit getMediaPlayer:playerId];
	    [mediaPlayer setVolume:volume type:ByteRTCAudioMixingTypePlayout];
	}
Objective-C
结束混音
	- (void)stopAudioMixing {
	    ByteRTCMediaPlayer *mediaPlayer = [self.rtcKit getMediaPlayer:playerId];
	    [mediaPlayer stop];
	}
Objective-C