1、CHiOSSpeech.h
复制
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
@protocol CHiOSSpeechDelegate <NSObject>
@optional
- (void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didStartSpeechUtterance:(AVSpeechUtterance*)utterance;
- (void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance*)utterance;
- (void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didPauseSpeechUtterance:(AVSpeechUtterance*)utterance;
- (void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didContinueSpeechUtterance:(AVSpeechUtterance*)utterance;
- (void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didCancelSpeechUtterance:(AVSpeechUtterance*)utterance;
@end
@interface CHiOSSpeech : NSObject
@property (nonatomic, weak) id<CHiOSSpeechDelegate> delegate;
+ (CHiOSSpeech *)sharedInstance;
- (void)startSpeekWithString:(NSString *)string;
- (void)pauseSpeaking;
- (void)continueSpeaking;
- (void)setDefaultWithVolume:(float)aVolume
rate:(CGFloat)aRate
pitchMultiplier:(CGFloat)aPitchMultiplier
languageCode:(NSString *)languageCode;
@end
2、CHiOSSpeech.m
#import "CHiOSSpeech.h"
@interface CHiOSSpeech () <AVSpeechSynthesizerDelegate>
{
AVSpeechSynthesizer *av;
AVSpeechUtterance *utterance;
}
@property(nonatomic, assign) float rate;
@property(nonatomic, assign) float volume;
@property(nonatomic, assign) float pitchMultiplier;
@property(nonatomic, copy) NSString *languageCode;
@end
@implementation CHiOSSpeech
+ (CHiOSSpeech *)sharedInstance {
static CHiOSSpeech *sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedClient = [[CHiOSSpeech alloc] init];
});
return sharedClient;
}
- (instancetype)init {
self = [super init];
if (self) {
av = [[AVSpeechSynthesizer alloc] init];
av.delegate = self;
[self setDefaultWithVolume:-1.0 rate:-1.0 pitchMultiplier:-1.0 languageCode:@"zh-CN"];
};
return self;
}
- (void)startSpeekWithString:(NSString *)string {
utterance = [[AVSpeechUtterance alloc] initWithString:string];
utterance.rate = 0.5;
AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:self.languageCode];
utterance.voice = voice;
utterance.rate = self.rate;
utterance.volume = self.volume;
utterance.pitchMultiplier = self.pitchMultiplier;
utterance.postUtteranceDelay = 1;
[av speakUtterance:utterance];
}
- (void)pauseSpeaking {
[av pauseSpeakingAtBoundary:AVSpeechBoundaryWord];
}
- (void)continueSpeaking {
[av continueSpeaking];
}
#pragma mark ***************************** AVSpeechSynthesizerDelegate ***********************************************
- (void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didStartSpeechUtterance:(AVSpeechUtterance*)utterance {
CHLog(@"是否正在播放:%d", synthesizer.isSpeaking);
CHLog(@"是否处于播放:%d", synthesizer.isPaused);
CHLog(@"播放的内容:%@", utterance.speechString);
CHLog(@"---开始播放");
if([self.delegate respondsToSelector:@selector(speechSynthesizer: didStartSpeechUtterance:)]) {
[self.delegate speechSynthesizer:synthesizer didStartSpeechUtterance:utterance];
}
}
- (void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance*)utterance {
CHLog(@"---完成播放");
if([self.delegate respondsToSelector:@selector(speechSynthesizer: didFinishSpeechUtterance:)]) {
[self.delegate speechSynthesizer:synthesizer didFinishSpeechUtterance:utterance];
}
}
- (void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didPauseSpeechUtterance:(AVSpeechUtterance*)utterance {
CHLog(@"---播放中止");
if([self.delegate respondsToSelector:@selector(speechSynthesizer: didPauseSpeechUtterance:)]) {
[self.delegate speechSynthesizer:synthesizer didPauseSpeechUtterance:utterance];
}
}
- (void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didContinueSpeechUtterance:(AVSpeechUtterance*)utterance {
CHLog(@"---恢复播放");
if([self.delegate respondsToSelector:@selector(speechSynthesizer: didContinueSpeechUtterance:)]) {
[self.delegate speechSynthesizer:synthesizer didContinueSpeechUtterance:utterance];
}
}
- (void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didCancelSpeechUtterance:(AVSpeechUtterance*)utterance {
CHLog(@"---播放取消");
if([self.delegate respondsToSelector:@selector(speechSynthesizer: didCancelSpeechUtterance:)]) {
[self.delegate speechSynthesizer:synthesizer didCancelSpeechUtterance:utterance];
}
}
- (void)setDefaultWithVolume:(float)aVolume rate:(CGFloat)aRate pitchMultiplier:(CGFloat)aPitchMultiplier languageCode:(NSString *)languageCode {
self.rate = aRate;
self.volume = aVolume;
self.pitchMultiplier = aPitchMultiplier;
self.languageCode = languageCode;
if (aRate == -1.0) {
self.rate = 0.5;
}
if (aVolume == -1.0) {
self.volume = 1.0;
}
if (aPitchMultiplier == -1.0) {
self.pitchMultiplier = 1;
}
}
@end
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~