歌曲评分

更新时间: 2022/12/06 08:41:04

网易云信正版曲库支持歌曲评分功能。您可以通过集成 NEPitchKit 组件快捷实现含 UI 的歌曲评分功能,您也可以通过 API 接口实现歌曲评分能力,并自行实现相关 UI 界面。

功能介绍

歌曲评分包括如下功能:

  • 歌曲播放时,同步回调对应的音高数据。
  • 单句歌词播放结束,返回实时得分数据。
  • 单首歌曲播放结束,返回整首歌曲的得分数据。
歌曲评分

前提条件

请确认您已完成以下操作:

实现歌曲评分(含UI)

功能原理

歌曲评分UI组件的架构示意图如下图所示。

歌曲评分组件的架构.png

注意事项

不需要执行 destory 操作,页面销毁的时候,NEPitchKit 组件内部会进行销毁操作。

集成 NEPitchUIKit 组件

  1. 导入 NEPitchUIKit 组件。

    pod 'NEPitchUIKit'
    
  2. 初始化 NEPitchRecordComponentView页面 , 添加到当前视图上 ,并设置相关代理。

///页面初始化
self.compoentView = [[NEPitchRecordComponentView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 300)];;
///添加到当前视图
    [self.view addSubview:self.compoentView];
///设置代理
    self.compoentView.delegate = self;
  1. 初始化相关数据。
///获取字歌词资源内容:
NSString *lyricContent = [NSString
      stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"xxx.txt" ofType:nil]
                      encoding:NSUTF8StringEncoding
                        error:nil];
///构造歌词数据:
  NELyric *model = [[NELyric alloc] initWithContent:lyricContent andType:NELyricTypeYrc];

///获取打分资源内容:
  NSString *midiContentString = [NSString
      stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"xxx.txt" ofType:nil]
                      encoding:NSUTF8StringEncoding
                        error:nil];
  1. 加载相关配置内容。
///separator 为打分内容的分割符 ;例如 打分内容为 xxx,xxx,xxx ... 则分割符为 " , "。
[self.compoentView
      loadRecordDataWithPitchContent:midiContentString
                          separator:@","
                          startTime:nil
                            endTime:nil
                          LocalLyric:lyricContent
                            andType:NELyricTypeYrc builder:nil];
  1. 调用 timeForCompoentView 方法实现代理。
///staticTime 为音乐的播放时间戳 
- (NSInteger)timeForCompoentView:(NEPitchRecordComponentView *)compoentView {
  return staticTime;
}

///如果使用NERtc进行播放,则按照如下方法获取到时间戳
///1.创建 RTC 房间,并加入
///2.播放音乐
///3.通过调用 RTC `getAudioMixingCurrentPosition` 的方法获取当前播放时间戳 ,具体RTC接入方式请参考官网
///第3步 获取方法如下 :
uint64_t currentTime;
int value = [[NERtcEngine sharedEngine] getAudioMixingCurrentPosition:&currentTime];
  1. 推送打分数据。

    1. 采集音频裸数据。

      如果您使用的是网易云信的 NERTC,请参考以下示例代码。如果您使用的是其他RTC产品,请自行实现相关逻辑。

      ///1.实现RTC 代理方法 `onNERtcEngineAudioFrameDidRecord`,数据类型为 `NERtcAudioFrame`。
      ///2.将数据类型`NERtcAudioFrame`转化为 `NEPitchAudioData`,具体代码如下所示:
      ///3.`stop` 为是否为最后一个数据传入的标记位,如果是最后一个数据,需要用户设置为YES,最终打分需要依赖最后一个数据属性已经传入。
      NEPitchAudioData *audioData = [[NEPitchAudioData alloc] init];
      if (self.stop) {
            audioData.done = YES;
            self.sendStop = YES;
            NSLog(@"最后一个数据输入");
            [[RTCManager getInstance] stopAudioMixing];
          } else {
            audioData.done = NO;
          }
      
          void *data = frame.data;
          audioData.samples = &data;
          audioData.validSampleCount = frame.format.samplesPerChannel;
      
          audioData.timeStamp = (int32_t)[[RTCManager getInstance] getAudioMixingCurrentPosition] - 10;
          audioData.sampleRate = frame.format.sampleRate;
          audioData.channelCount = frame.format.channels;
          //    NSLog(@"数据推送 ---- %d",audioData.done);
          [self.compoentView pushAudioData:audioData];
      
    2. 调用 pushAudioData 接口推送打分数据。

    void *data = 采集的音频裸数据;
    audioData.samples = &data;
    audioData.validSampleCount = frame.format.samplesPerChannel;
    
    audioData.timeStamp = (int32_t)[[RTCManager getInstance] getAudioMixingCurrentPosition] - 10;
    audioData.sampleRate = frame.format.sampleRate;
    audioData.channelCount = frame.format.channels;
    //    NSLog(@"数据推送 ---- %d",audioData.done);
    [self.compoentView pushAudioData:audioData];
    

定制 UI 界面

UI Kit 支持自定义界面的元素,包括颜色、线形状、分数蹦出效果、分数格式等。

在步骤 4 加载相关配置内容时,可以通过设置 builder 相关属性,实现自定义 UI 界面,示例代码如下:

 [self.compoentView
      loadRecordDataWithPitchContent:midiContentString
                           separator:@","
                           startTime:nil
                             endTime:nil
                          LocalLyric:content
                             andType:NELyricTypeYrc
                             builder:^(NEPitchLayoutBuilder *_Nonnull builder) {
                               NSString *path = [[NSBundle mainBundle] pathForResource:@""
                                                                                ofType:nil];
                               builder.pitchImagePath = path;
                               builder.emitterPathArray = @[
                                 [[NSBundle mainBundle] pathForResource:@"pop_1.png" ofType:nil],
                                 [[NSBundle mainBundle] pathForResource:@"pop_2.png" ofType:nil],
                                 [[NSBundle mainBundle] pathForResource:@"pop_3.png" ofType:nil]
                               ];
                               builder.finalScorePathArray = @[
                                 [[NSBundle mainBundle] pathForResource:@"score-s.webp" ofType:nil],
                                 [[NSBundle mainBundle] pathForResource:@"score-sss.webp"
                                                                 ofType:nil],
                                 [[NSBundle mainBundle] pathForResource:@"score-ss.webp" ofType:nil]
                               ];
                               builder.onceTimeScorePathArray = @[
                                 [[NSBundle mainBundle] pathForResource:@"score-nice.json"
                                                                 ofType:nil],
                                 [[NSBundle mainBundle] pathForResource:@"score-perfect.json"
                                                                 ofType:nil],
                                 [[NSBundle mainBundle] pathForResource:@"score-cool.json"
                                                                 ofType:nil]
                               ];
                               NSString *resource =
                                   [[NSBundle mainBundle] pathForResource:@"sing_score_pic_x.png"
                                                                   ofType:nil];

                               builder.perfectScorePathArray = @[ resource ];
                             }];

相关属性及规则说明如下表所示。

属性名 含义 规范
lineHeight 音阶线高度 默认4
eachTimeLength 音阶线缩放长度 默认0.08
UIColor * (^drawColorBlock)(void) 音调重合渲染颜色
UIColor * (^bottomColorBlock)(void) 基础底色渲染
UIColor * (^LinearGradientStartColorBlock)(void) 遮罩层渐变颜色的开始颜色
UIColor * (^LinearGradientEndColorBlock)(void) 遮罩层渐变颜色的结束颜色
pitchImagePath 自定义箭头的文件路径 完整路径
emitterPathArray 自定义气泡样式的文件路径 建议3个文件路径
finalScorePathArray 最终打分格式的文件路径 文件命名规范 xxx-sss.webp;xxx-ss.webp;xxx-s.webp
尺寸设置为 628 x 540
onceTimeScorePathArray 单次评分格式的文件路径 文件命名规范 xxx-perfect.json;xxx-nice.json;xxx-cool.json
perfectScorePathArray 演唱perfect时 追加的 x1 到 x9 到 xn图片 文件命名规范 xxxx_score_pic_x ;xxxx_score_pic_0;xxxx_score_pic_1;...
尺寸设置为 20 x 20

实现歌曲评分(不含UI)

功能原理

uml diagram

实现方法

  1. 初始化歌曲评分组件。

    1. 调用 NECopyrightedMedia getInstance 接口创建版权音乐对象。

      NECopyrightedMedia * copyRight = [NECopyrightedMedia getInstance];
      
      
    2. 调用 initialize 接口初始化歌曲评分组件。

      //初始化版权SDK
      [[NECopyrightedMedia getInstance] initialize:copyrightedAppKey
                                          token:copyrightedToken
                                        userUuid:account
                                          extras:nil];
      
      
  2. 初始化打分引擎。

    1. 调用 NEPitchSongScore getInstance 接口创建打分引擎对象。
      NEPitchSongScore * songScore = [NEPitchSongScore getInstance];
      
    2. 调用 createInfoWithPitchContent 接口生成 NEPitchRecordSingInfo 对象。
    NEPitchRecordSingInfo *info = [NEPitchRecordSingInfo createInfoWithPitchContent:打分内容文本
                                              separator:打分内容文本分割符
                                              startTime:开始时间 (完整歌曲 填入nil 或 0)
                                                endTime:结束时间 (完整歌曲填入nil或歌曲时长 ,单位是毫秒)
                                             LocalLyric:歌词内容文本
                                                andType:歌词类型];
    
    1. 调用 initialize 接口初始化引擎对象。
    [[NEPitchSongScore getInstance] initialize:info];
    
  3. 调用 start接口启动打分功能。

    [[NEPitchSongScore getInstance] start];
    
  4. 持续调用 pushAudioData 接口进行数据推送,驱动打分功能运行。

    //NEKaraokeRtcAudioFrame * frame 为 RTC回调数据
    // BOOL isEnd 为是否最后一个数据 ,业务流程处理,唱完歌后,最后一次数据传递设置为YES
    // _recorStart 为了保证最后一次数据传入后后续数据不再push
    //收到采集的数据后调用如下方法,最后这一次数据需要设置 .done = YES
    - (void)pushAudioFrameWithFrame:(NEKaraokeRtcAudioFrame *)frame isEnd:(BOOL)isEnd{
        if (_recorStart) {
            if (isEnd) {
                _recorStart = NO;
                NSLog(@"发送最后一次数据");
            }
            NEPitchAudioData *audioData = [[NEPitchAudioData alloc] init];
            audioData.done = isEnd;
            void * data = frame.data;
            audioData.samples = &data;
            audioData.validSampleCount = frame.format.samplesPerChannel;
            audioData.timeStamp = (int32_t)self.timeForCurrent() - 10;
            audioData.sampleRate = frame.format.sampleRate;
            audioData.channelCount = frame.format.channels;
            [[NEPitchSongScore getInstance] pushAudioData:audioData];
        }
    }
    
  5. 通过 markerReceiveNoteBlock 回调,获取实时音高数据。

    [NEPitchSongScore getInstance].onNote = ^(NEPitchRecordItemModel * _Nonnull item) {
                
    };
    
  6. 通过 markerReceiveGradeBlock 回调,获取实时打分数据。

    [NEPitchSongScore getInstance].onGrade = ^(NEPitchRecordSingMarkModel * _Nonnull markModel) {
                
    };
    
  7. 调用 getFinalScoreComplete 接口计算最终分数。

    [[NEPitchSongScore getInstance]
          getFinalScoreComplete:^(NSError *_Nullable error,
                                  NEPitchRecordSingInfo *_Nullable pitchRecordSingInfo) {
            __block long markValue = pitchRecordSingInfo.chorusFinalMark.totalValue /
                                    pitchRecordSingInfo.availableLyricCount;
            __block NEOpusLevel markLevel = NEOpusLevelC;
            dispatch_async(dispatch_get_main_queue(), ^{
              if (markValue > 90) {
                markLevel = NEOpusLevelSSS;
              } else if (markValue > 80) {
                markLevel = NEOpusLevelSS;
              } else {
                markLevel = NEOpusLevelS;
              }
              //结束 开始打分
              [NEKaraokeSongLog successLog:karaokeSongLog desc:@"展示打分"];
              @strongify(self)[self.lyricActionView lyricActionViewLevel:markLevel
                                                            resultModel:playResultModel];
            });
          }]
    
  8. 调用 destroy 接口销毁打分器。

    [[NEPitchSongScore getInstance]] destroy];
    

其他接口

//暂停打分
[[NEPitchSongScore getInstance] pause];

//录唱过程中seek,就是跳过一些句子或者重新从某个句子开始唱,需要刷新一下打分库。
[[NEPitchSongScore getInstance] seekTime:startTime];

//注:seek之后需要100ms之后调用resetMidi方法,打分库响应时间需要
[[NEPitchSongScore getInstance] resetMidi];
此文档是否对你有帮助?
有帮助
去反馈
  • 功能介绍
  • 前提条件
  • 实现歌曲评分(含UI)
  • 功能原理
  • 注意事项
  • 集成 NEPitchUIKit 组件
  • 定制 UI 界面
  • 实现歌曲评分(不含UI)
  • 功能原理
  • 实现方法
  • 其他接口