跳到主要内容

混音

前提条件

从网络音源混音的能力依赖 RongRTCPlayer 插件。SDK 需要使用以下插件将网络文件下载后播放。如有需要,请集成以下插件:

// x.y.z,请填写具体的 SDK 版本号,新集成用户建议使用 SDK 和插件的最新版。
pod 'RongCloudRTC/RongRTCPlayer', '~> x.y.z' 融云 CDN + 混音网络资源文件(可选)

开始混音

混音功能支持将用户自定义的音频数据与本地麦克风采集的音频数据进行混合,加入房间并发布默认的音频流后调用 startMixingWithURL:playback:mixerMode:loopCount: 方法混音指定的音频文件。

- (BOOL)startMixingWithURL:(NSURL *)fileURL
playback:(BOOL)isPlay
mixerMode:(RCRTCMixerMode)mode
loopCount:(NSUInteger)count;
参数类型说明
fileURLNSURL本地文件或者网络资源 URL。注意,参数 fileURL 为网络资源时,在线资源混音需要依赖 RongRTCPlayer 插件。
isPlayBOOL是否在本地播放
modeRCRTCMixerMode混音方式
countNSUInteger循环混音或者播放次数

混音成功后,返回 YES。

//NSURL *mediaUrl = [NSURL URLWithString:@"https://xxxx.mp3"];
NSString *audioFilePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"mp3"];
NSURL *mediaUrl = [NSURL fileURLWithPath:audioFilePath];
[[RCRTCAudioMixer sharedInstance] startMixingWithURL:mediaUrl
playback:YES
mixerMode:RCRTCMixerModeMixing
loopCount:NSUIntegerMax];

切换左右声道

提示

SDK 从 5.1.13 开始支持切换左右声道。仅支持在与本地文件混音时切换声道。不支持网络资源文件。

与本地文件混音时,可以调用 setAudioDualMonoMode 单独选择混音的左右声道。如未调用默认模式为使用立体声混音。

例如调用 setAudioDualMonoMode 设置为左声道混音时,SDK 会拷贝当前音频的左声道数据填充右声道,进而两个声道都是左声道数据,开始混音后用户听到的就是当前左声道混音的声音。您可以通过左右声道切换实现原唱、伴唱切换,以满足卡拉 OK 场景下的需求(通过声道切换原唱、伴唱要求音源支持,即原唱、伴唱音轨分别位于两个独立声道)。

/*!
设置混音声道模式
@param mode 声道模式 0 立体声混音, 1 左声道混音, 2 右声道混音
@discussion
只针对本地文件资源混音播放产生效果, 不支持网络资源的 url
Add from 5.1.13

@remarks 音频配置
@return 设置是否成功
*/
- (BOOL)setAudioDualMonoMode:(RCRTCAudioDualMonoMode)mode;

mode 为 SDK 提供的混音模式(RCRTCAudioDualMonoMode),支持选择左右声道或者立体声。

设置成功后返回 YES,设置即时生效。

RCRTCAudioDualMonoMode mode = RCRTCAudioDualMonoLeft;//RCRTCAudioDualMonoRight
[[RCRTCAudioMixer sharedInstance] setAudioDualMonoMode:mode]

控制混音状态(结束、暂停、恢复)

  • 混音结束或结束混音时需要调用以下方法结束混音。

    [[RCRTCAudioMixer sharedInstance] stop];
  • 暂停混音

    [[RCRTCAudioMixer sharedInstance] pause];
  • 恢复混音

    [[RCRTCAudioMixer sharedInstance] resume];
  • 获取当前混音状态

    if(RTCMixEngineStatusPlaying == [RCRTCAudioMixer sharedInstance].status) {
    // 处理正在播放混音的逻辑
    }

设置混音播放位置

提示

从 5.2.5.1 开始,SDK 支持先设置播放位置(setPlayProgress),再调用开始混音(startMixingWithURL)。

在混音开始前后均可以设置播放位置。如果希望从混音文件指定位置开始混音,建议先设置播放位置,再调用开始混音。

[[RCRTCAudioMixer sharedInstance] setPlayProgress:progress];
参数类型说明
progressfloat设置播放进度 取值范围 [0,1]

音量控制

SDK 支持控制混音音源输入的音量和混音在本地播放的音量。

调节混音输入音量

设置音频文件混音时的输入音量,取值范围 [0,100],默认值 100。

// 设置音频文件混音输入音量为 50
[RCRTCAudioMixer sharedInstance].mixingVolume = 50;

调节混音本地播放音量

如果设置混音时选择了本地播放,您可以调节音频文件本地播放音量,取值范围 [0,100],默认值 100。

// 设置音频文件本地播放音量为 50
[RCRTCAudioMixer sharedInstance].playingVolume = 50;

获取音频文件时长

SDK 支持获取混音音频文件时长。网络资源 URL 在播放后才能拿到音频时长。

// 获取音频文件时长
Float64 duration = [RCRTCAudioMixer durationOfAudioFile:fileUrl];
参数类型说明
urlNSURL文件 URL 音频时长(网络资源 URL 在播放后才能拿到音频时长)

返回音频文件时长,单位为秒。

混音事件回调

设置混音播放代理,通过代理用户可以得到 播放/混音进度播放/混音状态

[RCRTCAudioMixer sharedEngine].delegate = self;
#pragma mark - RCRTCAudioMixerAudioPlayDelegate - AudioMixer 的播放代理

/*!
当前播放进度

@param progress 播放进度 range [0,1]
@discussion
当前播放进度

@remarks 代理
*/
- (void)didReportPlayingProgress:(float)progress{
NSLog(@"当前播放进度 - %@", @(progress));
}

/*!
混音状态

@param mixingState 混音状态
@param mixingReason 混音状态改变的原因
@discussion
当前混音状态
Add from 5.1.3

@remarks 代理
*/
- (void)didAudioMixingStateChanged:(RCRTCAudioMixingState)mixingState reason:(RCRTCAudioMixingReason)mixingReason{
if (mixingState == RCRTCMixingStatePlaying){
// 开始混音,可能的原因有:
if (mixingReason == RCRTCMixingReasonStartedByUser){
// 调用了开始混音方法 startMix
}else if (mixingReason == RCRTCMixingReasonStartNewLoop){
// 调用了 startMix 传入的loopCount < 0(无限循环)或 > 1时,自动开始下一次混音。
}else if (mixingReason == RCRTCMixingReasonResumedByUser){
// 调用了 resume 方法继续开始混音
}else if (mixingReason == RCRTCMixingReasonFileLoaded){
// 本地或网络混音文件已加载完成(SDK 必须等待资源加载完成后才能播放混音文件),该回调要求 SDK 版本 ≧ 5.2.5.1。
}
}else if(mixingState == RCRTCMixingStateStop){
// 混音完成,可能的原因有:
if (mixingReason == RCRTCMixingReasonAllLoopsCompleted){
// 调用 startMix方法时,传入的 loopCount > 0,并且 loopCount 次数的混音已经完成
}else if (mixingReason == RCRTCMixingReasonOneLoopCompleted){
// 调用 startMix方法时,传入的 loopCount < 0(无限循环)或 > 1,混音完成一次。接下来会继续自动开始下一次混音。
}else if (mixingReason == RCRTCMixingReasonStoppedByUser){
// 调用 stopMix 方法停止混音
}
}else if (mixingState == RCRTCMixingStatePause){
// 暂停了混音,mixingReason 为 RCRTCMixingReasonPausedByUser
}else if (mixingState == RCRTCMixingStateFailed){
// 加载失败,mixingReason 为 RCRTCMixingReasonCanNotOpen
}
}


@end

使用自定义音频数据进行混音

您可以通过写入自定义音频数据的方式进行混音。自定义音频数据可以使用 SDK 内置的格式要求或使用您自定义的格式。

使用内置音频格式写入自定义混音数据

  1. 通过以下该属性,获取方法 writeAudioBufferList:frames:sampleTime:playback: 写入 AudioBufferList 要求的格式。

    @property (nonatomic, readonly, class) AudioStreamBasicDescription writeAsbd;
  2. 向即将发送的音频数据中混合自定义音频数据。

    • 方法

      - (void)writeAudioBufferList:(const AudioBufferList*)abl
      frames:(UInt32)frames
      sampleTime:(SInt64)sampleTime
      playback:(BOOL)isPlay;

      abl 为 AudioBufferList 类型布局,必须使用上一步中获取的格式。

      参数类型说明
      ablAudioBufferList音频数据
      framesUInt32音频帧个数
      sampleTimeSInt64音频帧时间戳
      isPlayBOOL是否在本地播放
    • 示例代码:

      SInt64 sampleTime = 0;
      while (true) {
      CMSampleBufferRef sample = [_outAudioTrack copyNextSampleBuffer];
      if (!sample) {
      break;
      }
      AudioBufferList abl = {0};
      CMBlockBufferRef blockBuffer;
      CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sample,
      NULL,
      &abl,
      sizeof(abl),
      NULL,
      NULL,
      0,
      &blockBuffer);
      CMItemCount count = CMSampleBufferGetNumSamples(sample);
      [[RCRTCAudioMixer sharedEngine] writeAudioBufferList:&abl
      frames:(UInt32)count
      sampleTime:sampleTime
      playback:YES];
      sampleTime += count;
      CFRelease(blockBuffer);
      CFRelease(sample);
      if (_status == RongRTCFileVideoCapturerStatusStopped) {
      break;
      }
      }

使用自定义格式写入混音数据

提示

SDK 从 5.2.3 版本开始支持该功能。

如果您希望自行指定 AudioBufferList 数据的格式,可以使用以下方法写入混音数据。

/*!
写入自定义音频数据
@param abl 音频数据,格式为 PCM
@param frames 音频帧个数
@param sampleTime 音频帧时间戳
@param asbd 音频格式描述
@param isPlay 是否在本地播放

@discussion
写入自定义音频数据

@remarks 音频流处理
*/
- (void)writeAudioBufferList:(const AudioBufferList *)abl
frames:(UInt32)frames
sampleTime:(SInt64)sampleTime
asbd:(AudioStreamBasicDescription)asbd
playback:(BOOL)isPlay;