问题描述:
业物需求,需要使用类似支付宝收款到账的通知,之前采用iOS10.0的原线上ServiceExtension进行收到通知修改通知内容然后采用系统AVSpeechSynthesizer 进行播放,iOS10-12.1之前通知一切正常,当12.1一更新之后APP通知会只有通知栏没有通知语音,因此查阅文档采坑终于找到了解决方案 ,时间短任务重没时间写太多,写文章半小时前刚搞定,所以暂时先附上代码有需要的先解决,之后再修改详细文章。
解决思想
1.先使用讯飞语音合成SDK将文字转化为PCM格式文件(讯飞只支持这玩意)
2.然后用lame工具转为MP3格式
3.使用AudioService进行MP3播放(试过其他播放方式不行。。。)
代码(先凑合看后续更新)
// NotificationService.m
// JZServiceExtension
//
// Created by Jincang Lu on 2018/11/13.
// Copyright © 2018年 shanghai KaoPush Network Technology Co., Ltd. All rights reserved.
//
#import "NotificationService.h"
#import <AVFoundation/AVFoundation.h>
#import <iflyMSC/iflyMSC.h>
#import "lame.h"
#define kTime 0.6
static SystemSoundID soundID = 10;
#define IF_APPID @"自己申请"
API_AVAILABLE(ios(10.0))
@interface NotificationService ()<AVSpeechSynthesizerDelegate,IFlySpeechSynthesizerDelegate>
@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
@property (nonatomic, strong) IFlySpeechSynthesizer *iFlySpeechSynthesizer;
@end
@implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler API_AVAILABLE(ios(10.0)){
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
NSString *initString = [[NSString alloc] initWithFormat:@"appid=%@", IF_APPID];
[IFlySpeechUtility createUtility:initString];
// Modify the notification content here...
self.bestAttemptContent.title = [NSString stringWithFormat:@"%@", self.bestAttemptContent.title];
self.bestAttemptContent.body = [NSString stringWithFormat:@"%@", self.bestAttemptContent.body];
self.bestAttemptContent.sound = nil;
[self syntheticVoice:[NSString stringWithFormat:@"%@",self.bestAttemptContent.body]];
// self.bestAttemptContent.sound = [UNNotificationSound soundNamed:@"xindong.caf"];
}
- (void)syntheticVoice:(NSString *)string {
//获取语音合成单例
_iFlySpeechSynthesizer = [IFlySpeechSynthesizer sharedInstance];
//设置协议委托对象
_iFlySpeechSynthesizer.delegate = self;
//设置合成参数
//设置在线工作方式
[_iFlySpeechSynthesizer setParameter:[IFlySpeechConstant TYPE_CLOUD]
forKey:[IFlySpeechConstant ENGINE_TYPE]];
//设置音量,取值范围 0~100
[_iFlySpeechSynthesizer setParameter:@"100"
forKey: [IFlySpeechConstant VOLUME]];
//发音人,默认为”xiaoyan”,可以设置的参数列表可参考“合成发音人列表”
[_iFlySpeechSynthesizer setParameter:@"xiaoyan"
forKey: [IFlySpeechConstant VOICE_NAME]];
//保存合成文件名,如不再需要,设置为nil或者为空表示取消,默认目录位于library/cache下
[_iFlySpeechSynthesizer setParameter:@"xfffs.pcm"
forKey: [IFlySpeechConstant TTS_AUDIO_PATH]];
//启动合成会话
// [_iFlySpeechSynthesizer startSpeaking: @"接着到账111111111110元"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES);
NSString *path = [paths objectAtIndex:0];
NSLog(@"psth==%@",path);
[_iFlySpeechSynthesizer synthesize: string toUri:[NSString stringWithFormat:@"%@/xfffss.pcm",path]];
}
//合成结束
- (void) onCompleted:(IFlySpeechError *) error {
NSLog(@"合成完成--%@-%d",error.errorDesc,error.errorCode);
[self convertToMp3];
self.contentHandler(self.bestAttemptContent);
}
- (void)convertToMp3
{
NSString *fileName = [NSString stringWithFormat:@"/%@.mp3", @"yuyin"];
NSString *filePath = [[NSHomeDirectory() stringByAppendingFormat:@"/Documents/"] stringByAppendingPathComponent:fileName];
NSLog(@"%@",filePath);
// NSString *mp3Url = [NSURL URLWithString:filePath];
@try {
int read,write;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES);
NSString *path = [paths objectAtIndex:0];
//只读方式打开被转换音频文件
FILE *pcm = fopen([[NSString stringWithFormat:@"%@/xfffss.pcm",path] cStringUsingEncoding:1], "rb");
fseek(pcm, 4 * 1024, SEEK_CUR);//删除头,否则在前一秒钟会有杂音
//只写方式打开生成的MP3文件
FILE *mp3 = fopen([filePath cStringUsingEncoding:1], "wb");
const int PCM_SIZE = 8192;
const int MP3_SIZE = 8192;
short int pcm_buffer[PCM_SIZE * 2];
unsigned char mp3_buffer[MP3_SIZE];
//这里要注意,lame的配置要跟AVAudioRecorder的配置一致,否则会造成转换不成功
lame_t lame = lame_init();
lame_set_in_samplerate(lame, 11025.0);//采样率
lame_set_VBR(lame, vbr_default);
lame_init_params(lame);
do {
//以二进制形式读取文件中的数据
read = (int)fread(pcm_buffer, 2 * sizeof(short int), PCM_SIZE, pcm);
if (read == 0)
write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
else
write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE);
//二进制形式写数据到文件中 mp3_buffer:数据输出到文件的缓冲区首地址 write:一个数据块的字节数 1:指定一次输出数据块的个数 mp3:文件指针
fwrite(mp3_buffer, write, 1, mp3);
} while (read != 0);
lame_close(lame);
fclose(mp3);
fclose(pcm);
} @catch (NSException *exception) {
NSLog(@"%@",[exception description]);
} @finally {
NSLog(@"MP3生成成功!!!");
// NSString *str = [_iFlySpeechSynthesizer parameterForKey:[IFlySpeechConstant TTS_AUDIO_PATH]];
// NSString *str = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@/xfff",path] ofType:@"mp3"];
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/yuyin.mp3",[NSHomeDirectory() stringByAppendingFormat:@"/Documents/"]]];
AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(url), &soundID);
AudioServicesPlayAlertSound(soundID);
AudioServicesPlayAlertSoundWithCompletion(soundID, ^{
NSLog(@"播放完成");
});
}
}
//合成开始
- (void) onSpeakBegin {
}
//合成缓冲进度
- (void) onBufferProgress:(int) progress message:(NSString *)msg {
NSLog(@"-----进度---------%@",msg);
}
//合成播放进度
- (void) onSpeakProgress:(int) progress beginPos:(int)beginPos endPos:(int)endPos {
NSLog(@"====%d==%d=====",beginPos,endPos);
}
- (void)serviceExtensionTimeWillExpire {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
// self.contentHandler(self.bestAttemptContent);
}
@end```
extension plist里面需要加就可以播放了 如果音质变化需要自己去调试参数
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
QQ 873702616
截止11.16号 支付宝 收钱吧 12.1也没解决此问题 希望大家尽量先不要在这个方法上浪费太多时间了,上边这个方法提交版本会有问题,提供一种解决思路 希望大家有后续方案进行探讨