IOS开发 百度语音实现播报及IOS12.1后的播报功能问题与实现

iOS 百度语音实现播报及iOS12.1后的播报功能问题与实现

 

最近碰到个接收到推送要实现语音播报的需求,需要后台推送通知,APP客户端收到通知之后语音播放:“您的账户收到一笔巨款”的功能。使用到了Notification Service Extension服务。

在之前的记录使用AVSpeechUtterance 来进行语音播报。
文章地址:http://www.laileshuo.com/?p=1324

经过实验AVSpeechUtterance语音声音很奇怪,于是考虑使用百度语音合成来实现播报。使用到的即是语音合成系统(TTS):语音合成(Text To Speech,TTS):将文本合成为语音,即声音文件。

集成百度语音合成地址:https://ai.baidu.com/docs#/TTS-iOS-SDK/top

 

一、集成百度TTS

 

  • 加入sdk

下载百度语音合成的SDK,我们需要BDSClientLib中的libBaiduSpeechSDK,Headers中的TTS文件夹下的文件,Resource文件夹的资源。将这些文件放到NotificationService文件夹下

如图所示

alt text

  • 添加依赖库

使用到的依赖库libsqlite3.0.tbd、libiconv.2.4.0.tbd、libc++.tbd、libz.1.2.5.tbd、GLKit.framework、SystemConfiguration.framework、AudioToolbox.framework、AVFoundation.framework、CFNetwork.framework、CoreLocation.framework、CoreTelephony.framework

alt text

 

二、代码实现

 

在NotificationService上实现代码

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    
    // 语音合成,使用AVAudioPlayer播放,成功
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    
    [self configureSDK];
    
    // 这个info 内容就是通知信息携带的数据,后面我们取语音播报的文案,通知栏的title,以及通知内容都是从这个info字段中获取
    NSString *alert = self.bestAttemptContent.userInfo[@"alert"];
    
    // 播报语音
    //百度语音TTS
    NSError *err = nil;
    NSInteger sentenceID = [[BDSSpeechSynthesizer sharedInstance] speakSentence: alert withError:&err];
    NSLog(@"sentenceID:%ld error:%@",(long)sentenceID,err);
    
    self.contentHandler(self.bestAttemptContent);
}


-(void)configureSDK{
    NSLog(@"TTS version info: %@", [BDSSpeechSynthesizer version]);
    
    if (!self.baiduPlayer) {
        self.baiduPlayer = [[BDSBuiltInPlayer alloc] init];
        self.baiduPlayer.delegate = self;
    }
    
    [BDSSpeechSynthesizer setLogLevel:BDS_PUBLIC_LOG_VERBOSE];
    [[BDSSpeechSynthesizer sharedInstance] setSynthesizerDelegate:self];
    [self configureOnlineTTS];
    [self configureOfflineTTS];
}

-(void)configureOnlineTTS {
    [[BDSSpeechSynthesizer sharedInstance] setApiKey:NS_Baidu_API_KEY withSecretKey:NS_Baidu_SECRET_KEY];
    [[BDSSpeechSynthesizer sharedInstance] setSynthParam:@(BDS_SYNTHESIZER_SPEAKER_FEMALE) forKey:BDS_SYNTHESIZER_PARAM_SPEAKER];
}

-(void)configureOfflineTTS {
    
    NSError *err = nil;
    // 在这里选择不同的离线音库(请在XCode中Add相应的资源文件),同一时间只能load一个离线音库。根据网络状况和配置,SDK可能会自动切换到离线合成。
    NSString* offlineEngineSpeechData = [[NSBundle mainBundle] pathForResource:@"Chinese_And_English_Speech_Female" ofType:@"dat"];
    
    NSString* offlineChineseAndEnglishTextData = [[NSBundle mainBundle] pathForResource:@"Chinese_And_English_Text" ofType:@"dat"];
    
    err = [[BDSSpeechSynthesizer sharedInstance] loadOfflineEngine:offlineChineseAndEnglishTextData speechDataPath:offlineEngineSpeechData licenseFilePath:self.localPath withAppCode:NS_Baidu_APP_ID];
    if(err){
        return;
    }
}

 

三、测试

 

首先选中主app运行到手机上,之后运行NotificationService

最后在极光上进行推送。

alt text

最后收到播放声音。

 

四、特别注意的事情

 

下面是关于iOS12.1之后:在12.1之后,在这个推送扩展就无法在后台进行播放了.

 

1、在12.1之后 推送扩展就无法在后台进行播放

 

下图是官方给出的说明,之前给出这个拓展推送主要是为了丰富推送的UI样式,推送信息加密之类的,结果却被用做推送语音播报,所以就发了这个声明,在12.1之后,在这个推送扩展就无法在后台进行播放了.

alt text

所以 iOS12.1使用百度语音无法播报。

 

2、测试遇到的现象

/ 12.1版本,AVAudioPlayer后台播放会失败

NSString *path = [[NSBundle mainBundle] pathForResource:@"audio" ofType:@"mp3"];

NSURL *url = [NSURL fileURLWithPath:outPutFilePath];
            
self.myPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
            
self.myPlayer.delegate = self;
[self.myPlayer play];

// 12.1版本,AudioServicesPlayAlertSoundWithCompletion后台播放会失败

static SystemSoundID soundID = 0;

AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(url), &soundID);

AudioServicesPlayAlertSoundWithCompletion(soundID, ^{
   NSLog(@"播放完成");
});

既然12.1,NotificationService中无法进行后台播报,所以使用AVAudioPlayer、AudioServicesCreateSystemSoundID都会失败


如果在NotificationService中info.plist文件中加入

plist里面需要加UIBackgroundModes的 audio 就可以播放了


但是:
这个当打包上传到Appstore上就会出现错误了,提示说NotificationService中的UIBackgroundModes的Audio这个字段是非法的,无法添加的。

 

3、解决方案之修改通知的UNNotificationSound

 

在收到推送后,我们可以将消息拆成多个本地,每个通知NotificationContent的Sound都对应工程资源的一个音频文件。

alt text

代码所示:

- (void)playWithRegisterNotifications:(NSString *)content {
    NSArray *array = @[@"shoukuan",@"2",@"bai",@"5",@"shi",@"dian",@"0",@"8",@"yuan"];
    for (NSString *string in array) {
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        [self registerNotificationWithString:string completeHandler:^{
            //延迟大于1秒感觉更好一点
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                dispatch_semaphore_signal(semaphore);
            });
        }];
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    }
}

- (void)registerNotificationWithString:(NSString *)string completeHandler:(dispatch_block_t)complete {
    [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) {
        
        if (granted) {
            UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc]init];
            
            content.title = @"";
            content.subtitle = @"";
            content.body = @"";
            content.sound = [UNNotificationSound soundNamed:[NSString stringWithFormat:@"%@.mp3",string]];
            
            content.categoryIdentifier = [NSString stringWithFormat:@"categoryIndentifier%@",string];
            
            UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.01 repeats:NO];
            
            UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:[NSString stringWithFormat:@"categoryIndentifier%@",string] content:content trigger:trigger];
            
            [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
                
                if (error == nil) {
                    
                    if (complete) {
                        complete();
                    }
                }
            }];
        }
    }];
}

经过测试,可以播放,但是:
dispatch_after在0.25秒后执行一个本地通知,但是这个情况很容易出现第语音播放不完整的情况,如果时间设置过大,语音播放的语速就很慢,体验极差。声音的大小也无法调整。

这种情况下可以采用通用的提示,如:您有一笔收款。这样的语音提示体验会好点,但是不是最优的方式

 

4、关于支付宝或者微信语音播报

 

经过查找,大概率支付宝、微信使用的使用voip模式,通过查找微信与支付宝的ipa,ipa中配置的文件都UIBackgroundModes(后台模式)包含voip。

所以大概率支付宝与微信都使用的是Voip PushKit实现的收款的语音播报功能

之后也会调查下Voip PushKit实现的收款的语音播报功能,PushKit和极光推送还不太一样。持续关注中。。。

本文地址:http://www.laileshuo.com/?p=1347

博客地址:www.laileshuo.com

猜你喜欢

转载自blog.csdn.net/ljc_563812704/article/details/103404170