在用户使用 “一起看”、“一起听” 等互动娱乐类应用时,如果需要在通话过程中播放音效或音乐文件等,而且让房间内的其他成员也听到声音,需要使用混音功能。混音功能可以将麦克风采集的音频数据与音频文件、PCM 音频数据等合并为一路音频流后,发布到房间内。本文将介绍如何使用混音功能。
RTC SDK 提供了音效混音类和音乐混音类。
一般来说,对于短时间的音效(比如小于 20s),可以使用音效类;对于较长的音频或 PCM 数据,可以使用音乐类。
参见下表:
参考以下顺序,调用 API 实现此功能:
以下接口在音效管理类和音乐管理类中都有。
对同一个音频文件进行操作时,混音 ID 应保持一致
| 步骤 | 调用目标 | 接口 |
|---|---|---|
| 1 | 设置回调句柄 | setEventHandler |
| 2 | (可选)预加载音乐文件 | preload |
| 3 | 播放音乐文件 | start |
| 4 | 暂停播放 | pause |
| 5 | 恢复播放 | resume |
| 6 | 停止播放 | stop |
除以上功能以外,RTC SDK 还支持调整音频播放音量和声道,设置音乐播放进度等功能。
此功能仅在音乐管理类中实现。
参考以下顺序,调用 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-Cpreload 预加载音频文件,可以减少 CPU 占用。如果已预加载,确保播放时传入的 ID 与预加载时的一致;如果不再需要播放后,可以调用 unload 清理内存空间。setVolumeAll 接口,可以调节全部混音音量,PC端只能通过setVolume 接口调节指定混音音量。