add TencentTTS

This commit is contained in:
oscarz
2024-08-30 17:46:21 +08:00
parent 27c160beaf
commit 487aead433
30 changed files with 1558 additions and 14 deletions

View File

@ -25,6 +25,8 @@
5509CEF12BB54DD10056C5C2 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5509CEF02BB54DD10056C5C2 /* Config.swift */; };
550B85A22C2BC624008834E5 /* InitAPP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550B85A12C2BC623008834E5 /* InitAPP.swift */; };
551C8C342C79946700B1A88C /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 551C8C332C79946700B1A88C /* GoogleService-Info.plist */; };
555027392C81C0ED00A05441 /* QCloudTTS.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 555027382C81C0ED00A05441 /* QCloudTTS.xcframework */; };
5586E0882C80AD2D00026733 /* TTSManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5586E0872C80AD2D00026733 /* TTSManager.swift */; };
559E6D7C2C34EAE700C971B9 /* IapManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559E6D7B2C34EAE700C971B9 /* IapManager.swift */; };
559E6D7E2C35355200C971B9 /* LogManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559E6D7D2C35355200C971B9 /* LogManager.swift */; };
55A954A22BBBFD0C00BF181E /* GrammarData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A954A12BBBFD0C00BF181E /* GrammarData.swift */; };
@ -88,6 +90,9 @@
5509CEF02BB54DD10056C5C2 /* Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = "<group>"; };
550B85A12C2BC623008834E5 /* InitAPP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitAPP.swift; sourceTree = "<group>"; };
551C8C332C79946700B1A88C /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
555027382C81C0ED00A05441 /* QCloudTTS.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = QCloudTTS.xcframework; sourceTree = "<group>"; };
5586E0832C8092C400026733 /* AIGrammar-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AIGrammar-Bridging-Header.h"; sourceTree = "<group>"; };
5586E0872C80AD2D00026733 /* TTSManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TTSManager.swift; sourceTree = "<group>"; };
559E6D7B2C34EAE700C971B9 /* IapManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IapManager.swift; sourceTree = "<group>"; };
559E6D7D2C35355200C971B9 /* LogManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogManager.swift; sourceTree = "<group>"; };
55A954A12BBBFD0C00BF181E /* GrammarData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrammarData.swift; sourceTree = "<group>"; };
@ -118,6 +123,7 @@
55BC474F2C3A4E5E00120A7D /* ToastUI in Frameworks */,
67F268687FCD2E4F2F4462C0 /* Pods_AIGrammar.framework in Frameworks */,
55EF5C3E2C2263460060CE47 /* StoreKit.framework in Frameworks */,
555027392C81C0ED00A05441 /* QCloudTTS.xcframework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -177,6 +183,8 @@
5500A38E2BB3C7E80065A1D3 /* AIGrammar */ = {
isa = PBXGroup;
children = (
5586E0842C8093DF00026733 /* third-party */,
5586E0832C8092C400026733 /* AIGrammar-Bridging-Header.h */,
55BC47472C3A380C00120A7D /* CommView */,
55C73D7E2C157C2200041C66 /* AIGrammar.entitlements */,
55DAC6532BBA956100BDD4C8 /* GrammarSubView */,
@ -240,6 +248,7 @@
559E6D7B2C34EAE700C971B9 /* IapManager.swift */,
559E6D7D2C35355200C971B9 /* LogManager.swift */,
55E4E8F42C60CFFC00988503 /* CommFunc.swift */,
5586E0872C80AD2D00026733 /* TTSManager.swift */,
);
path = lib;
sourceTree = "<group>";
@ -253,6 +262,14 @@
path = ViewModel;
sourceTree = "<group>";
};
5586E0842C8093DF00026733 /* third-party */ = {
isa = PBXGroup;
children = (
555027382C81C0ED00A05441 /* QCloudTTS.xcframework */,
);
path = "third-party";
sourceTree = "<group>";
};
55BC47472C3A380C00120A7D /* CommView */ = {
isa = PBXGroup;
children = (
@ -551,6 +568,7 @@
5500A3922BB3C7E80065A1D3 /* ContentView.swift in Sources */,
55BB127D2BBD6D0600D2BEA4 /* CameraView.swift in Sources */,
559E6D7C2C34EAE700C971B9 /* IapManager.swift in Sources */,
5586E0882C80AD2D00026733 /* TTSManager.swift in Sources */,
55EF5C3C2C2250900060CE47 /* IAPTestView.swift in Sources */,
5500A3902BB3C7E80065A1D3 /* AIGrammarApp.swift in Sources */,
5500A39C2BB3C7EB0065A1D3 /* AIGrammar.xcdatamodeld in Sources */,
@ -724,6 +742,13 @@
DEVELOPMENT_ASSET_PATHS = "\"AIGrammar/Preview Content\"";
DEVELOPMENT_TEAM = G8UMWM9TLL;
ENABLE_PREVIEWS = YES;
EXCLUDED_ARCHS = x86_64;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/AIGrammar/third-party",
"$(PROJECT_DIR)/AIGrammar/third-party/ios-arm64_armv7",
"$(PROJECT_DIR)/AIGrammar/third-party/ios-arm64_i386_x86_64-simulator",
);
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = EasyGrammar;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
@ -738,11 +763,91 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-l\"c++\"",
"-l\"sqlite3\"",
"-l\"z\"",
"-framework",
"\"Alamofire\"",
"-framework",
"\"CFNetwork\"",
"-framework",
"\"CoreTelephony\"",
"-framework",
"\"Cryptor\"",
"-framework",
"\"CryptorECC\"",
"-framework",
"\"CryptorRSA\"",
"-framework",
"\"FBLPromises\"",
"-framework",
"\"FirebaseABTesting\"",
"-framework",
"\"FirebaseAnalytics\"",
"-framework",
"\"FirebaseCore\"",
"-framework",
"\"FirebaseCoreExtension\"",
"-framework",
"\"FirebaseCoreInternal\"",
"-framework",
"\"FirebaseCrashlytics\"",
"-framework",
"\"FirebaseInstallations\"",
"-framework",
"\"FirebasePerformance\"",
"-framework",
"\"FirebaseRemoteConfig\"",
"-framework",
"\"FirebaseRemoteConfigInterop\"",
"-framework",
"\"FirebaseSessions\"",
"-framework",
"\"FirebaseSharedSwift\"",
"-framework",
"\"Foundation\"",
"-framework",
"\"GoogleAppMeasurement\"",
"-framework",
"\"GoogleAppMeasurementIdentitySupport\"",
"-framework",
"\"GoogleDataTransport\"",
"-framework",
"\"GoogleUtilities\"",
"-framework",
"\"KituraContracts\"",
"-framework",
"\"LoggerAPI\"",
"-framework",
"\"Logging\"",
"-framework",
"\"Promises\"",
"-framework",
"\"Security\"",
"-framework",
"\"StoreKit\"",
"-framework",
"\"SwiftJWT\"",
"-framework",
"\"SwiftyBeaver\"",
"-framework",
"\"SystemConfiguration\"",
"-framework",
"\"TrustDecision\"",
"-framework",
"\"UIKit\"",
"-framework",
"\"nanopb\"",
);
PRODUCT_BUNDLE_IDENTIFIER = com.easyprompts.aigrammar;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/AIGrammar/AIGrammar-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
@ -760,6 +865,13 @@
DEVELOPMENT_ASSET_PATHS = "\"AIGrammar/Preview Content\"";
DEVELOPMENT_TEAM = G8UMWM9TLL;
ENABLE_PREVIEWS = YES;
EXCLUDED_ARCHS = x86_64;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/AIGrammar/third-party",
"$(PROJECT_DIR)/AIGrammar/third-party/ios-arm64_armv7",
"$(PROJECT_DIR)/AIGrammar/third-party/ios-arm64_i386_x86_64-simulator",
);
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = EasyGrammar;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
@ -774,11 +886,92 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-l\"c++\"",
"-l\"sqlite3\"",
"-l\"z\"",
"-framework",
"\"Alamofire\"",
"-framework",
"\"CFNetwork\"",
"-framework",
"\"CoreTelephony\"",
"-framework",
"\"Cryptor\"",
"-framework",
"\"CryptorECC\"",
"-framework",
"\"CryptorRSA\"",
"-framework",
"\"FBLPromises\"",
"-framework",
"\"FirebaseABTesting\"",
"-framework",
"\"FirebaseAnalytics\"",
"-framework",
"\"FirebaseCore\"",
"-framework",
"\"FirebaseCoreExtension\"",
"-framework",
"\"FirebaseCoreInternal\"",
"-framework",
"\"FirebaseCrashlytics\"",
"-framework",
"\"FirebaseInstallations\"",
"-framework",
"\"FirebasePerformance\"",
"-framework",
"\"FirebaseRemoteConfig\"",
"-framework",
"\"FirebaseRemoteConfigInterop\"",
"-framework",
"\"FirebaseSessions\"",
"-framework",
"\"FirebaseSharedSwift\"",
"-framework",
"\"Foundation\"",
"-framework",
"\"GoogleAppMeasurement\"",
"-framework",
"\"GoogleAppMeasurementIdentitySupport\"",
"-framework",
"\"GoogleDataTransport\"",
"-framework",
"\"GoogleUtilities\"",
"-framework",
"\"KituraContracts\"",
"-framework",
"\"LoggerAPI\"",
"-framework",
"\"Logging\"",
"-framework",
"\"Promises\"",
"-framework",
"\"Security\"",
"-framework",
"\"StoreKit\"",
"-framework",
"\"SwiftJWT\"",
"-framework",
"\"SwiftyBeaver\"",
"-framework",
"\"SystemConfiguration\"",
"-framework",
"\"TrustDecision\"",
"-framework",
"\"UIKit\"",
"-framework",
"\"nanopb\"",
);
PRODUCT_BUNDLE_IDENTIFIER = com.easyprompts.aigrammar;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/AIGrammar/AIGrammar-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};

View File

@ -4,7 +4,8 @@
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
@ -62,7 +63,9 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
allowLocationSimulation = "YES"
consoleMode = "0"
structuredConsoleMode = "1">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference

View File

@ -0,0 +1,14 @@
//
// AIGrammar-Bridging-Header.h
// AIGrammar
//
// Created by oscar on 2024/8/29.
//
#ifndef AIGrammar_Bridging_Header_h
#define AIGrammar_Bridging_Header_h
#import "QCloudTTS/QCloudTTSEngine.h"
#import "QCloudTTS/QCloudMediaPlayer2.h"
#endif /* AIGrammar_Bridging_Header_h */

View File

@ -8,6 +8,7 @@
import SwiftUI
import AVFoundation
import ToastUI
import FirebaseAnalytics
struct SubmitTextEditor: UIViewRepresentable {
@Binding var text: String
@ -271,7 +272,9 @@ struct TranslateCardView: View {
var onEdit: (String) -> Void
var onCopy: (String) -> Void
var onFeedback: (TranslateFeedback) -> Void
let synthesizer = AVSpeechSynthesizer()
//
@State private var isSynthesizing = false
var body: some View {
VStack {
@ -285,15 +288,15 @@ struct TranslateCardView: View {
Text(translation.input)
.padding(.bottom, 3)
HStack {
/*
Button(action: {
speakText(translation.input)
synthesizeAndPlay(text: translation.input, lang: "Original")
}) {
Image(systemName: "speaker.wave.2")
.foregroundColor(.blue)
.padding(.trailing)
}
*/
Spacer()
Button(action: {
onEdit(translation.input)
@ -315,14 +318,14 @@ struct TranslateCardView: View {
.padding(.vertical, 1) // 线
.padding(.bottom, 3) //
HStack {
/*
Button(action: {
speakText(translation.translation)
synthesizeAndPlay(text: translation.translation, lang: "Translated")
}) {
Image(systemName: "speaker.wave.2")
.foregroundColor(.blue)
}
*/
Spacer()
HStack(spacing: 15) {//
Button(action: {
@ -362,14 +365,27 @@ struct TranslateCardView: View {
.background(Color.gray.opacity(0.2)) // LightGray
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.gray, lineWidth: 1))
}
func speakText(_ text: String) {
let utterance = AVSpeechUtterance(string: text)
utterance.voice = AVSpeechSynthesisVoice(language: "en-US")
synthesizer.speak(utterance)
func synthesizeAndPlay(text: String, lang: String) {
//
Analytics.logEvent("TTSClick", parameters: [
"vendor": "TencentTTS",
"lang" : lang
])
isSynthesizing = true
TTSManager.shared.synthesizeAndPlay(text: text, isVIP: globalEnvironment.isVip, onSuccess: {
isSynthesizing = false
logger.info("Synthesis succ.")
}, onFailure: { error in
isSynthesizing = false
logger.info("Synthesis error: \(error)")
})
}
}
extension View {
func dashed() -> some View {
self.overlay(

View File

@ -100,6 +100,7 @@ class GlobalAnalyticsEvents: ObservableObject {
let valEntryBuyProBtn = "buy_pro_btn"
let valEntrySettingsBtn = "settings_buy_btn"
}
let globalAnalyticsEvents = GlobalAnalyticsEvents()

View File

@ -0,0 +1,100 @@
//
// TTSManager.swift
// AIGrammar
//
// Created by oscar on 2024/8/29.
//
import Foundation
import AVFAudio
class TTSManager: NSObject, QCloudTTSEngineDelegate {
static let shared = TTSManager() //
private var synthesizer: QCloudTTSEngine?
private var audioPlayer: AVAudioPlayer?
private var onSuccess: (() -> Void)?
private var onFailure: ((String) -> Void)?
// 使
private let dailyLimit = 100
private let userDefaults = UserDefaults.standard
private override init() {
super.init()
setupSynthesizer()
}
private func setupSynthesizer() {
synthesizer = QCloudTTSEngine.getShareInstance() as? QCloudTTSEngine
synthesizer?.engineInit(.TTS_MODE_ONLINE, delegate: self)
synthesizer?.setOnlineAuthParam(1300230265, secretId: "AKIDmcetGuREGiDOBeIJVm4Kd1ZOUqFdD1CQ", secretKey: "UwhP3XrJfrx8IQUfpMvRznYTK5lM4rhR", token: nil)
}
func synthesizeAndPlay(text: String, isVIP: Bool, onSuccess: @escaping () -> Void, onFailure: @escaping (String) -> Void) {
if !isVIP {
let currentDate = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let currentDateString = dateFormatter.string(from: currentDate)
let lastDateString = userDefaults.string(forKey: "LastCallDate") ?? ""
var callCount = userDefaults.integer(forKey: "CallCount")
if currentDateString != lastDateString {
//
callCount = 0
userDefaults.set(currentDateString, forKey: "LastCallDate")
}
logger.info("nonVIP check limit, cnt: \(callCount), limit: \(dailyLimit)")
if callCount >= dailyLimit {
onFailure("Daily limit reached. Please try again tomorrow.")
return
} else {
//
callCount += 1
userDefaults.set(callCount, forKey: "CallCount")
}
}
guard let synthesizer = synthesizer else {
onFailure("Synthesizer is not initialized.")
return
}
self.onSuccess = onSuccess
self.onFailure = onFailure
let error = synthesizer.synthesize(text, utteranceId: "test")
if let error = error {
onFailure("Synthesis error: \(error)")
}
}
// MARK: - QCloudTTSEngineDelegate
func onSynthesizeData(_ data: Data?, utteranceId: String?, text: String?, engineType: Int, requestId: String?, respJson: String?) {
guard let data = data else {
onFailure?("Synthesis failed with no data.")
return
}
do {
audioPlayer = try AVAudioPlayer(data: data)
audioPlayer?.prepareToPlay()
audioPlayer?.play()
onSuccess?()
} catch {
onFailure?("Failed to play synthesized audio: \(error)")
}
}
func onError(_ error: TtsError?, utteranceId: String?, text: String?) {
if let error = error {
onFailure?("Synthesis error: \(error)")
}
}
func onOfflineAuthInfo(_ OfflineAuthInfo: QCloudOfflineAuthInfo) {
// 线
}
}

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>LibraryIdentifier</key>
<string>ios-arm64_i386_x86_64-simulator</string>
<key>LibraryPath</key>
<string>QCloudTTS.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>i386</string>
<string>x86_64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
<dict>
<key>LibraryIdentifier</key>
<string>ios-arm64_armv7</string>
<key>LibraryPath</key>
<string>QCloudTTS.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>armv7</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>

View File

@ -0,0 +1,25 @@
//
// QPlayerError.h
// cloud-tts-sdk-ios
//
// Created by renqiu on 2022/1/11.
//
#import <Foundation/Foundation.h>
typedef NS_ENUM (NSInteger,QCPlayerErrorCode){
QPLAYER_ERROR_CODE_EXCEPTION = -201,
QPLAYER_ERROR_CODE_PLAY_QUEUE_IS_FULL = -202,
QPLAYER_ERROR_CODE_AUDIO_READ_FAILEDL = -203,
QPLAYER_ERROR_CODE_UNKNOW = -204,
};
@interface QCPlayerError : NSObject
@property (nonatomic, assign) NSInteger mCode;
@property (nonatomic,copy)NSString *message;
@property (nonatomic,strong)NSError *errorMessage;
+(instancetype)getQCPlayerErrorWithMcode:(NSInteger)mCode ErrorMessage:(NSError*)errorMessage;
@end

View File

@ -0,0 +1,47 @@
//
// QCloudMediaPlayer.h
// cloud-tts-sdk-ios
//
// Created by renqiu on 2022/1/11.
//
#import <Foundation/Foundation.h>
#import <QCloudTTS/QCPlayerError.h>
#import <QCloudTTS/QCloudPlayerDelegate.h>
/// <#Description#>
@interface QCloudMediaPlayer : NSObject
@property (assign,nonnull)id <QCloudPlayerDelegate>playerDelegate;
//
/// 数据入队列
/// @param data 加入队列的音频
/// @param text 音频对应的文本
/// @param utteranceId 音频对应的ID
-(void)enqueueWithData:(NSData* _Nonnull )data Text:(NSString* _Nullable)text UtteranceId:(NSString* _Nullable)utteranceId;
//
/// 数据入队列
/// @param data 加入队列的音频
/// @param text 音频对应的文本
/// @param utteranceId 音频对应的ID
/// @param server 服务端返回的信息,包含Subtitles时会使用Subtitle来进行时间戳判断
-(void)enqueueWithData:(NSData* _Nonnull )data Text:(NSString* _Nullable)text UtteranceId:(NSString* _Nullable)utteranceId RespJson:(NSString* _Nullable)respjson;
/// 数据入队列
/// @param file 加入队列的音频文件
/// @param text 音频文件对应的文本
/// @param utteranceId 音频文件对应的ID
-(void)enqueueWithFile:(NSURL* _Nullable)file Text:(NSString* _Nullable)text UtteranceId:(NSString* _Nullable)utteranceId;
//
/// 停止播放
-(QCPlayerError* _Nullable)StopPlay;
/// 暂停播放
-(QCPlayerError* _Nullable)PausePlay;
/// 恢复播放
-(QCPlayerError* _Nullable)ResumePlay;
-(NSInteger)getAudioQueueSize;
@end

View File

@ -0,0 +1,35 @@
//
// QCloudMediaPlayer.h
// cloud-tts-sdk-ios
//
// Created by renqiu on 2022/1/11.
//
#import <Foundation/Foundation.h>
#import <QCloudTTS/QCPlayerError.h>
#import <QCloudTTS/QCloudMediaPlayer.h>
/// <#Description#>
@interface QCloudMediaPlayer2 : NSObject
@property (weak)id <QCloudPlayerDelegate> _Nullable playerDelegate;
//
/// 数据入队列
/// @param data 加入队列的音频
/// @param text 音频对应的文本
/// @param utteranceId 音频对应的ID
-(void)enqueueWithData:(NSData* _Nonnull )data Text:(NSString* _Nullable)text UtteranceId:(NSString* _Nullable)utteranceId;
/// 数据入队列
/// @param file 加入队列的音频文件
/// @param text 音频文件对应的文本
/// @param utteranceId 音频文件对应的ID
-(void)enqueueWithFile:(NSURL* _Nullable)file Text:(NSString* _Nullable)text UtteranceId:(NSString* _Nullable)utteranceId;
//
/// 停止播放
-(QCPlayerError* _Nullable)StopPlay;
/// 暂停播放
-(QCPlayerError* _Nullable)PausePlay;
/// 恢复播放
-(QCPlayerError* _Nullable)ResumePlay;
-(NSInteger)getAudioQueueSize;
@end

View File

@ -0,0 +1,48 @@
//
// QCloudOfflineAuthInfo.h
// QCloudTTS
//
// Created by dgw on 2022/6/15.
//
#import <Foundation/Foundation.h>
/*如果您下载的是在线版TTS SDK ,请忽略此接口文件*/
NS_ASSUME_NONNULL_BEGIN
// 离线授权错误吗
typedef NS_ENUM(NSInteger, AuthErrorCode) {
OFFLINE_AUTH_SUCCESS = 0,//"Auth Success"
OFFLINE_AUTH_NETWORK_CONNECT_FAILED = -10,//"Network connect failed."
OFFLINE_AUTH_NETWORK_SERVER_AUTH_FAILED = -11,//"Server Authorization Error See Response Message."
OFFLINE_AUTH_PARAMETERS_ERROR = -12,//"Parameter cannot be empty."
OFFLINE_AUTH_PACKAGENAME_ERROR = -13,//"Authorization package name error."
OFFLINE_AUTH_DEVICE_ID_ERROR = -14,//"Authorization device ID error."
OFFLINE_AUTH_GET_DEVICE_ID_FAILED = -15,//"The device is abnormal and the device ID cannot be obtained."
OFFLINE_AUTH_PLATFORM_ERROR = -16,//"The platform is not authorized."
OFFLINE_AUTH_BIZCODE_ERROR = -17,//"License business does not match."
OFFLINE_AUTH_EXPIRED = -18,//"Authorization has expired.")
OFFLINE_AUTH_JSON_PARSE_FAILED = -19,//"JSON Parsing Error."
OFFLINE_AUTH_DECODE_ERROR = -20,//"Could not decode license, please check input parameters."
OFFLINE_AUTH_UNKNOWN_ERROR = -21,//"Unknown authorization error."
};
// 离线授权信息类
@interface QCloudOfflineAuthInfo : NSObject
@property (nonatomic, copy, nullable) NSString* respose; //使用在线拉取授权时服务器返回到json数据
@property (nonatomic, assign) AuthErrorCode err_code; //错误码
@property (nonatomic, copy, nullable) NSString* err_msg; //错误信息
@property (nonatomic, copy, nullable) NSString* deviceId; //设备id
@property (nonatomic, copy, nullable) NSString* expireTime; //到期时间
@property (nonatomic, copy, nullable) NSString* voiceAuthList; //已授权的音色名列表,用分号隔开
+(instancetype _Nonnull )getQCloudOfflineAuthInfo:(AuthErrorCode)err_code Respose:(NSString*_Nullable)respose;
+(NSString* _Nonnull )getErrorMsg:(AuthErrorCode)code;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,37 @@
#import <Foundation/Foundation.h>
#import <QCloudTTS/QCPlayerError.h>
@protocol QCloudPlayerDelegate <NSObject>
//播放开始
-(void) onTTSPlayStart;
//队列所有音频播放完成,音频等待中
-(void) onTTSPlayWait;
//恢复播放
-(void) onTTSPlayResume;
//暂停播放
-(void) onTTSPlayPause;
//播放中止
-(void)onTTSPlayStop;
//即将播放播放下一句即将播放音频对应的句子以及这句话utteranceId
/// 即将播放播放下一句即将播放音频对应的句子以及这句话utteranceId
/// @param text 当前播放句子的文本
/// @param utteranceId 当前播放音频对应的ID
-(void) onTTSPlayNextWithText:(NSString* _Nullable)text UtteranceId:(NSString* _Nullable)utteranceId;
//播放器异常
-(void)onTTSPlayError:(QCPlayerError* _Nullable)playError;
/// 当前播放的字符,当前播放的字符在所在的句子中的下标.
/// @param currentWord 当前读到的单个字,是一个估算值不一定准确
/// @param currentIdex 当前播放句子中读到文字的下标
-(void)onTTSPlayProgressWithCurrentWord:(NSString*_Nullable)currentWord CurrentIndex:(NSInteger)currentIdex;
@end

View File

@ -0,0 +1,222 @@
//
// QCloudTTSEngine.h
// cloud-tts-sdk-ios
//
// Created by dgw on 2022/1/10.
//
#import <Foundation/Foundation.h>
#import <QCloudTTS/TtsError.h>
#import <QCloudTTS/QCloudOfflineAuthInfo.h>
typedef NS_ENUM(NSUInteger, TtsMode) {
TTS_MODE_ONLINE = 0, //在线模式
TTS_MODE_OFFLINE = 1, //离线模式
TTS_MODE_MIX = 2 //离在线混合模式
};
/// 回调接口
@protocol QCloudTTSEngineDelegate <NSObject>
@optional
/// 合成结果回调
/// @param data 音频数据
/// @param utteranceId utteranceId
/// @param text text
/// @param type 该句话是以何种引擎生成的0:在线 1:离线
/// @deprecated
-(void) onSynthesizeData:(NSData *_Nullable)data UtteranceId:(NSString *_Nullable)utteranceId Text:(NSString *_Nullable)text EngineType:(NSInteger)type;
/// 合成结果回调
/// @param data 音频数据
/// @param utteranceId utteranceId
/// @param text text
/// @param type 该句话是以何种引擎生成的0:在线 1:离线
/// @param requestId 请求ID仅engineType为0时不为nil用于排查问题
-(void) onSynthesizeData:(NSData *_Nullable)data UtteranceId:(NSString *_Nullable)utteranceId Text:(NSString *_Nullable)text EngineType:(NSInteger)type RequestId:(NSString* _Nullable)requestId;
/// 合成结果回调
/// @param data 音频数据
/// @param utteranceId utteranceId
/// @param text text
/// @param type 该句话是以何种引擎生成的0:在线 1:离线
/// @param respJson 请求结果
-(void) onSynthesizeData:(NSData *_Nullable)data UtteranceId:(NSString *_Nullable)utteranceId Text:(NSString *_Nullable)text EngineType:(NSInteger)type RequestId:(NSString* _Nullable)requestId RespJson:(NSString* _Nullable)respJson;
@required
/// 错误回调
/// @param error 错误信息
/// @param utteranceId utteranceId
/// @param text text
-(void) onError:(TtsError *_Nullable)error UtteranceId:(NSString *_Nullable)utteranceId Text:(NSString *_Nullable)text;
/// 返回离线合成模块授权信息,使用混合或者离线模式时,收到此方法成功回调后才可以调用合成接口
/// 如果您下载的是在线版TTS SDK ,或者只使用在线合成模式,请忽略此方法
/// @param OfflineAuthInfo 授权信息当OfflineAuthInfo.err_msg为0时授权成功详见QCloudOfflineAuthInfo.h
-(void) onOfflineAuthInfo:(QCloudOfflineAuthInfo* _Nonnull )OfflineAuthInfo;
@end
/// 语音合成引擎接口
@interface QCloudTTSEngine : NSObject
/// 获得QCloudTTSEngine实例
+(id _Nullable )getShareInstance;
/// 销毁QCloudTTSEngine实例
+(void) instanceRelease;
/// 初始化引擎
/// @param mode 引擎模式在线、离线、混合如切换引擎需要先执行instanceRelease
/// @param delegate 用于接收结果的代理
-(void) engineInit:(TtsMode)mode Delegate:(id<QCloudTTSEngineDelegate> _Nonnull) delegate;
/// 取消合成,清空内部合成队列
-(TtsError *_Nullable) cancel;
/// 合成接口,支持持续调用添加文本
/// @param text 需要合成的文本
/// @param utteranceId 用于标记文本的id合成完成后随音频文件或者错误接口返回,可为空
-(TtsError *_Nullable) synthesize:(NSString *_Nonnull)text UtteranceId:(NSString *_Nullable)utteranceId;
/// 配置在线引擎鉴权参数
/// @param appId appId
/// @param secretId secretId
/// @param secretKey secretKey
/// @param token token可为空如果使用sts临时证书鉴权secretId和secretKey均入参临时的同时需要入参对应的token
/// sts临时证书具体详见https://cloud.tencent.com/document/product/598/33416 开发调试时建议先用普通方式鉴权token填nil
-(void) setOnlineAuthParam:(NSInteger)appId SecretId:(NSString* _Nonnull)secretId SecretKey:(NSString* _Nonnull)secretKey Token:(NSString* _Nullable)token;
///online语音相关设置
/// 设置在线语速,对setOnlineParam的封装
/// @param voiceSpeed 默认0代表正常速度
-(void)setOnlineVoiceSpeed:(float)voiceSpeed;
/// 设置在线音色ID,对setOnlineParam的封装
/// @param voiceType 默认1001音色id可查看官网文档https://cloud.tencent.com/document/product/1073/37995
-(void)setOnlineVoiceType:(int)voiceType;
/// 设置在线引擎音量,对setOnlineParam的封装
/// @param voiceVolume 默认0代表正常音量没有静音选项
-(void)setOnlineVoiceVolume:(float)voiceVolume;
/// 设置主语言类型,对setOnlineParam的封装
/// @param primaryLanguage 1:中文 2:英文 默认1
-(void)setOnlineVoiceLanguage:(int)primaryLanguage;
///在线编码格式,如无业务特殊需求不建议更改(默认"mp3",目前支持"mp3","wav","pcm",若更改为pcm不支持直接播放,对setOnlineParam的封装
-(void)setOnlineCodec:(NSString* _Nonnull) code;
/// 项目ID
/// @param projectId 默认0, 对setOnlineParam的封装
-(void)setOnlineProjectId:(int)projectId;
/// 是否开启时间戳功能默认为false,对setOnlineParam的封装
- (void)setOnlineEnableSubtitle:(Boolean)val;
/// 断句敏感阈值默认值为0,对setOnlineParam的封装
- (void)setOnlineSegmentRate:(int)val;
/// 控制合成音频的情感,仅支持多情感音色使用,对setOnlineParam的封装
- (void)setOnlineEmotionCategory:(NSString* _Nullable)val;
/// 控制合成音频情感程度,对setOnlineParam的封装
- (void)setOnlineEmotionIntensity:(int)val;
/// 设置自定义参数,可使用该方法控制请求时的参数
/// @param value为nil时将删除参数,否则会在请求中添加参数
- (void)setOnlineParam:(NSString* _Nonnull)key value:(NSObject* _Nullable)value;
/// 设置区域
/// @param region 默认为ap-shanghai,无特殊需求请勿更改
- (void)setOnlineRegion:(NSString *)region;
//设置是否输出日志
-(void)setEnableDebugLog:(BOOL)enableDebugLog;
/// timeoutIntervalForRequest ,0.5s - 30s 默认15000ms(15s)
/// @param timeout [2200,60000] 单位ms
- (void)setTimeoutIntervalForRequest:(int)timeout;
/// timeoutIntervalForResource 2.2s - 60s 默认30000ms(30s)
/// @param timeout [500,30000] 单位ms
-(void)setTimeoutIntervalForResource:(int) timeout;
/// Mix模式下出现网络错误后的检测间隔时间, 默认值5分钟
///注意:每次检测时将使用所入参的一句文本请求服务器,如果后端合成成功将会额外消耗该句字数的合成额度
/// @param checkNetworkIntervalTime 大于等于0 单位s, 等于0时持续检测直到成功
-(void)setCheckNetworkIntervalTime:(int) checkNetworkIntervalTime;
//以下为离线语音相关参数配置
//resourceDir 离线资源路径
-(void)setOfflineResourceDir:(NSString * _Nonnull) resourceDir;
//voiceSpeed [0.5,2.0]
-(void)setOfflineVoiceSpeed:(float) voiceSpeed;
//voiceType 离线音色名称,名称配置位于音色资源目录\voices\config.json 中可自行指定更多的音色demo中仅提供"pb"、"femalen"两种
-(void)setOfflineVoiceType:(NSString *_Nonnull) voiceType;
//voiceVolume >0
-(void)setOfflineVoiceVolume:(float) voiceVolume;
/// 配置离线TTS授权参数: 在线下载密钥(第一次激活设备需要联网)
/// @param licPk 密钥对应的licPk,请在腾讯云官网页面获取,或者由腾讯云商务线下下发
/// @param licKey 密钥对应的licKey,请在腾讯云官网页面获取,或者由腾讯云商务线下下发
/// @param secretId 腾讯云secretId 可能与在线模式不是同一个账号需要输入购买离线SDK对应的账号的secretId
/// @param secretKey 腾讯云 secretKey可能与在线模式不是同一个账号需要输入购买离线SDK对应的账号的secretKey
/// @param token token可为空如果使用sts临时证书鉴权secretId和secretKey均入参临时的同时需要入参对应的token
-(void)setOfflineAuthParamDoOnline:(NSString* _Nonnull)licPk
LicKey:(NSString* _Nonnull)licKey
SecretId:(NSString* _Nonnull)secretId
SecretKey:(NSString* _Nonnull)secretKey
Token:(NSString* _Nullable)token;
/// 配置离线TTS授权参数: 在线下载密钥(第一次激活设备需要联网)
/// @param licPk 密钥对应的licPk,请在腾讯云官网页面获取,或者由腾讯云商务线下下发
/// @param licKey 密钥对应的licKey,请在腾讯云官网页面获取,或者由腾讯云商务线下下发
/// @param secretId 腾讯云secretId 可能与在线模式不是同一个账号需要输入购买离线SDK对应的账号的secretId
/// @param secretKey 腾讯云 secretKey可能与在线模式不是同一个账号需要输入购买离线SDK对应的账号的secretKey
/// @param token token可为空如果使用sts临时证书鉴权secretId和secretKey均入参临时的同时需要入参对应的token
/// @param refreshAuth 是否强制联网刷新授权(NO:仅第一次联网激活下载授权文件; YES:联网刷新授权文件,无网络下将激活失败)
-(void)setOfflineAuthParamDoOnline:(NSString* _Nonnull)licPk
LicKey:(NSString* _Nonnull)licKey
SecretId:(NSString* _Nonnull)secretId
SecretKey:(NSString* _Nonnull)secretKey
Token:(NSString* _Nullable)token
RefreshAuth:(BOOL)refreshAuth;
/// 配置离线TTS授权参数: 直接传入密钥(第一次激活也不需要联网)
/// @param lic 授权密钥 ,请在腾讯云官网页面获取,或者由腾讯云商务线下下发
/// @param licPk 该密钥对应的licPk请在腾讯云官网页面获取或者由腾讯云商务线下下发
/// @param licSign 该密钥对应的licSign请在腾讯云官网页面获取或者由腾讯云商务线下下发
-(void)setOfflineAuthParamDoOffline:(NSString* _Nonnull)lic
LicPk:(NSString* _Nonnull)licPk
LicSign:(NSString* _Nonnull)licSign;
@end

View File

@ -0,0 +1,70 @@
//
// TtsError.h
// cloud-tts-sdk-ios
//
// Created by dgw on 2022/1/10.
//
#ifndef TtsError_h
#define TtsError_h
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, TtsErrorCode) {
TTS_ERROR_CODE_UNINITIALIZED = -100, //Engine uninitialized
TTS_ERROR_CODE_GENERATE_SIGN_FAIL = -101, //generate sign failed
TTS_ERROR_CODE_NETWORK_CONNECT_FAILED = 102, //network connect failed
TTS_ERROR_CODE_DECODE_FAIL = -103, //JSON Response Parsing Error
TTS_ERROR_CODE_SERVER_RESPONSE_ERROR = -104, //Server response error
TTS_ERROR_CODE_CANCEL_FAILURE = -106, //Cancel failureplease try again later
TTS_ERROR_CODE_OFFLINE_FAILURE = -107,//Offline synthesize failed, please check your text and VoiceType
TTS_ERROR_CODE_OFFLINE_INIT_FAILURE = -108,//Offline engine initialization failed, please check resource files
TTS_ERROR_CODE_OFFLINE_AUTH_FAILURE = -109,//Offline engine auth failure
TTS_ERROR_CODE_OFFLINE_TEXT_TOO_LONG = -110, //Offline synthesize failed,text too long,MAX <= 1024 byte
TTS_ERROR_CODE_OFFLINE_VOICE_AUTH_FAILURE = -111, //This voice not authorized, please change it
TTS_ERROR_CODE_OFFLINE_NOSUPPORT = -900 //This SDK only supports online mode
};
/// 后端返回的错误信息
@interface TtsServiceError : NSObject
@property (nonatomic, copy) NSString* _Nullable err_code;
@property (nonatomic, copy) NSString* _Nullable msg;
@property (nonatomic, copy) NSString* _Nullable respose;
+(instancetype _Nonnull)getTtsServiceError:(NSString *_Nullable)err_code ErrorMsg:(NSString* _Nullable)msg Respose:(NSString* _Nullable)respose;
@end
/// 错误信息类
@interface TtsError : NSObject
@property (nonatomic, assign) TtsErrorCode err_code; //错误码
@property (nonatomic, copy, nullable) NSString* msg; //错误信息
@property (nonatomic, copy, nullable) NSError * error; //系统抛出的错误信息(不一定有,可能为空)
@property (nonatomic, strong ,nullable) TtsServiceError * serviceError;//服务器返回的错误信息(不一定有,可能为空)
+(instancetype _Nonnull)getTtsError:(TtsErrorCode)err_code
TtsServiceError:(TtsServiceError* _Nullable)serviceError
NSErrorMsg:(NSError *_Nullable)error;
@end
#endif /* TtsError_h */

View File

@ -0,0 +1,25 @@
//
// QPlayerError.h
// cloud-tts-sdk-ios
//
// Created by renqiu on 2022/1/11.
//
#import <Foundation/Foundation.h>
typedef NS_ENUM (NSInteger,QCPlayerErrorCode){
QPLAYER_ERROR_CODE_EXCEPTION = -201,
QPLAYER_ERROR_CODE_PLAY_QUEUE_IS_FULL = -202,
QPLAYER_ERROR_CODE_AUDIO_READ_FAILEDL = -203,
QPLAYER_ERROR_CODE_UNKNOW = -204,
};
@interface QCPlayerError : NSObject
@property (nonatomic, assign) NSInteger mCode;
@property (nonatomic,copy)NSString *message;
@property (nonatomic,strong)NSError *errorMessage;
+(instancetype)getQCPlayerErrorWithMcode:(NSInteger)mCode ErrorMessage:(NSError*)errorMessage;
@end

View File

@ -0,0 +1,47 @@
//
// QCloudMediaPlayer.h
// cloud-tts-sdk-ios
//
// Created by renqiu on 2022/1/11.
//
#import <Foundation/Foundation.h>
#import <QCloudTTS/QCPlayerError.h>
#import <QCloudTTS/QCloudPlayerDelegate.h>
/// <#Description#>
@interface QCloudMediaPlayer : NSObject
@property (assign,nonnull)id <QCloudPlayerDelegate>playerDelegate;
//
/// 数据入队列
/// @param data 加入队列的音频
/// @param text 音频对应的文本
/// @param utteranceId 音频对应的ID
-(void)enqueueWithData:(NSData* _Nonnull )data Text:(NSString* _Nullable)text UtteranceId:(NSString* _Nullable)utteranceId;
//
/// 数据入队列
/// @param data 加入队列的音频
/// @param text 音频对应的文本
/// @param utteranceId 音频对应的ID
/// @param server 服务端返回的信息,包含Subtitles时会使用Subtitle来进行时间戳判断
-(void)enqueueWithData:(NSData* _Nonnull )data Text:(NSString* _Nullable)text UtteranceId:(NSString* _Nullable)utteranceId RespJson:(NSString* _Nullable)respjson;
/// 数据入队列
/// @param file 加入队列的音频文件
/// @param text 音频文件对应的文本
/// @param utteranceId 音频文件对应的ID
-(void)enqueueWithFile:(NSURL* _Nullable)file Text:(NSString* _Nullable)text UtteranceId:(NSString* _Nullable)utteranceId;
//
/// 停止播放
-(QCPlayerError* _Nullable)StopPlay;
/// 暂停播放
-(QCPlayerError* _Nullable)PausePlay;
/// 恢复播放
-(QCPlayerError* _Nullable)ResumePlay;
-(NSInteger)getAudioQueueSize;
@end

View File

@ -0,0 +1,35 @@
//
// QCloudMediaPlayer.h
// cloud-tts-sdk-ios
//
// Created by renqiu on 2022/1/11.
//
#import <Foundation/Foundation.h>
#import <QCloudTTS/QCPlayerError.h>
#import <QCloudTTS/QCloudMediaPlayer.h>
/// <#Description#>
@interface QCloudMediaPlayer2 : NSObject
@property (weak)id <QCloudPlayerDelegate> _Nullable playerDelegate;
//
/// 数据入队列
/// @param data 加入队列的音频
/// @param text 音频对应的文本
/// @param utteranceId 音频对应的ID
-(void)enqueueWithData:(NSData* _Nonnull )data Text:(NSString* _Nullable)text UtteranceId:(NSString* _Nullable)utteranceId;
/// 数据入队列
/// @param file 加入队列的音频文件
/// @param text 音频文件对应的文本
/// @param utteranceId 音频文件对应的ID
-(void)enqueueWithFile:(NSURL* _Nullable)file Text:(NSString* _Nullable)text UtteranceId:(NSString* _Nullable)utteranceId;
//
/// 停止播放
-(QCPlayerError* _Nullable)StopPlay;
/// 暂停播放
-(QCPlayerError* _Nullable)PausePlay;
/// 恢复播放
-(QCPlayerError* _Nullable)ResumePlay;
-(NSInteger)getAudioQueueSize;
@end

View File

@ -0,0 +1,48 @@
//
// QCloudOfflineAuthInfo.h
// QCloudTTS
//
// Created by dgw on 2022/6/15.
//
#import <Foundation/Foundation.h>
/*如果您下载的是在线版TTS SDK ,请忽略此接口文件*/
NS_ASSUME_NONNULL_BEGIN
// 离线授权错误吗
typedef NS_ENUM(NSInteger, AuthErrorCode) {
OFFLINE_AUTH_SUCCESS = 0,//"Auth Success"
OFFLINE_AUTH_NETWORK_CONNECT_FAILED = -10,//"Network connect failed."
OFFLINE_AUTH_NETWORK_SERVER_AUTH_FAILED = -11,//"Server Authorization Error See Response Message."
OFFLINE_AUTH_PARAMETERS_ERROR = -12,//"Parameter cannot be empty."
OFFLINE_AUTH_PACKAGENAME_ERROR = -13,//"Authorization package name error."
OFFLINE_AUTH_DEVICE_ID_ERROR = -14,//"Authorization device ID error."
OFFLINE_AUTH_GET_DEVICE_ID_FAILED = -15,//"The device is abnormal and the device ID cannot be obtained."
OFFLINE_AUTH_PLATFORM_ERROR = -16,//"The platform is not authorized."
OFFLINE_AUTH_BIZCODE_ERROR = -17,//"License business does not match."
OFFLINE_AUTH_EXPIRED = -18,//"Authorization has expired.")
OFFLINE_AUTH_JSON_PARSE_FAILED = -19,//"JSON Parsing Error."
OFFLINE_AUTH_DECODE_ERROR = -20,//"Could not decode license, please check input parameters."
OFFLINE_AUTH_UNKNOWN_ERROR = -21,//"Unknown authorization error."
};
// 离线授权信息类
@interface QCloudOfflineAuthInfo : NSObject
@property (nonatomic, copy, nullable) NSString* respose; //使用在线拉取授权时服务器返回到json数据
@property (nonatomic, assign) AuthErrorCode err_code; //错误码
@property (nonatomic, copy, nullable) NSString* err_msg; //错误信息
@property (nonatomic, copy, nullable) NSString* deviceId; //设备id
@property (nonatomic, copy, nullable) NSString* expireTime; //到期时间
@property (nonatomic, copy, nullable) NSString* voiceAuthList; //已授权的音色名列表,用分号隔开
+(instancetype _Nonnull )getQCloudOfflineAuthInfo:(AuthErrorCode)err_code Respose:(NSString*_Nullable)respose;
+(NSString* _Nonnull )getErrorMsg:(AuthErrorCode)code;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,37 @@
#import <Foundation/Foundation.h>
#import <QCloudTTS/QCPlayerError.h>
@protocol QCloudPlayerDelegate <NSObject>
//播放开始
-(void) onTTSPlayStart;
//队列所有音频播放完成,音频等待中
-(void) onTTSPlayWait;
//恢复播放
-(void) onTTSPlayResume;
//暂停播放
-(void) onTTSPlayPause;
//播放中止
-(void)onTTSPlayStop;
//即将播放播放下一句即将播放音频对应的句子以及这句话utteranceId
/// 即将播放播放下一句即将播放音频对应的句子以及这句话utteranceId
/// @param text 当前播放句子的文本
/// @param utteranceId 当前播放音频对应的ID
-(void) onTTSPlayNextWithText:(NSString* _Nullable)text UtteranceId:(NSString* _Nullable)utteranceId;
//播放器异常
-(void)onTTSPlayError:(QCPlayerError* _Nullable)playError;
/// 当前播放的字符,当前播放的字符在所在的句子中的下标.
/// @param currentWord 当前读到的单个字,是一个估算值不一定准确
/// @param currentIdex 当前播放句子中读到文字的下标
-(void)onTTSPlayProgressWithCurrentWord:(NSString*_Nullable)currentWord CurrentIndex:(NSInteger)currentIdex;
@end

View File

@ -0,0 +1,222 @@
//
// QCloudTTSEngine.h
// cloud-tts-sdk-ios
//
// Created by dgw on 2022/1/10.
//
#import <Foundation/Foundation.h>
#import <QCloudTTS/TtsError.h>
#import <QCloudTTS/QCloudOfflineAuthInfo.h>
typedef NS_ENUM(NSUInteger, TtsMode) {
TTS_MODE_ONLINE = 0, //在线模式
TTS_MODE_OFFLINE = 1, //离线模式
TTS_MODE_MIX = 2 //离在线混合模式
};
/// 回调接口
@protocol QCloudTTSEngineDelegate <NSObject>
@optional
/// 合成结果回调
/// @param data 音频数据
/// @param utteranceId utteranceId
/// @param text text
/// @param type 该句话是以何种引擎生成的0:在线 1:离线
/// @deprecated
-(void) onSynthesizeData:(NSData *_Nullable)data UtteranceId:(NSString *_Nullable)utteranceId Text:(NSString *_Nullable)text EngineType:(NSInteger)type;
/// 合成结果回调
/// @param data 音频数据
/// @param utteranceId utteranceId
/// @param text text
/// @param type 该句话是以何种引擎生成的0:在线 1:离线
/// @param requestId 请求ID仅engineType为0时不为nil用于排查问题
-(void) onSynthesizeData:(NSData *_Nullable)data UtteranceId:(NSString *_Nullable)utteranceId Text:(NSString *_Nullable)text EngineType:(NSInteger)type RequestId:(NSString* _Nullable)requestId;
/// 合成结果回调
/// @param data 音频数据
/// @param utteranceId utteranceId
/// @param text text
/// @param type 该句话是以何种引擎生成的0:在线 1:离线
/// @param respJson 请求结果
-(void) onSynthesizeData:(NSData *_Nullable)data UtteranceId:(NSString *_Nullable)utteranceId Text:(NSString *_Nullable)text EngineType:(NSInteger)type RequestId:(NSString* _Nullable)requestId RespJson:(NSString* _Nullable)respJson;
@required
/// 错误回调
/// @param error 错误信息
/// @param utteranceId utteranceId
/// @param text text
-(void) onError:(TtsError *_Nullable)error UtteranceId:(NSString *_Nullable)utteranceId Text:(NSString *_Nullable)text;
/// 返回离线合成模块授权信息,使用混合或者离线模式时,收到此方法成功回调后才可以调用合成接口
/// 如果您下载的是在线版TTS SDK ,或者只使用在线合成模式,请忽略此方法
/// @param OfflineAuthInfo 授权信息当OfflineAuthInfo.err_msg为0时授权成功详见QCloudOfflineAuthInfo.h
-(void) onOfflineAuthInfo:(QCloudOfflineAuthInfo* _Nonnull )OfflineAuthInfo;
@end
/// 语音合成引擎接口
@interface QCloudTTSEngine : NSObject
/// 获得QCloudTTSEngine实例
+(id _Nullable )getShareInstance;
/// 销毁QCloudTTSEngine实例
+(void) instanceRelease;
/// 初始化引擎
/// @param mode 引擎模式在线、离线、混合如切换引擎需要先执行instanceRelease
/// @param delegate 用于接收结果的代理
-(void) engineInit:(TtsMode)mode Delegate:(id<QCloudTTSEngineDelegate> _Nonnull) delegate;
/// 取消合成,清空内部合成队列
-(TtsError *_Nullable) cancel;
/// 合成接口,支持持续调用添加文本
/// @param text 需要合成的文本
/// @param utteranceId 用于标记文本的id合成完成后随音频文件或者错误接口返回,可为空
-(TtsError *_Nullable) synthesize:(NSString *_Nonnull)text UtteranceId:(NSString *_Nullable)utteranceId;
/// 配置在线引擎鉴权参数
/// @param appId appId
/// @param secretId secretId
/// @param secretKey secretKey
/// @param token token可为空如果使用sts临时证书鉴权secretId和secretKey均入参临时的同时需要入参对应的token
/// sts临时证书具体详见https://cloud.tencent.com/document/product/598/33416 开发调试时建议先用普通方式鉴权token填nil
-(void) setOnlineAuthParam:(NSInteger)appId SecretId:(NSString* _Nonnull)secretId SecretKey:(NSString* _Nonnull)secretKey Token:(NSString* _Nullable)token;
///online语音相关设置
/// 设置在线语速,对setOnlineParam的封装
/// @param voiceSpeed 默认0代表正常速度
-(void)setOnlineVoiceSpeed:(float)voiceSpeed;
/// 设置在线音色ID,对setOnlineParam的封装
/// @param voiceType 默认1001音色id可查看官网文档https://cloud.tencent.com/document/product/1073/37995
-(void)setOnlineVoiceType:(int)voiceType;
/// 设置在线引擎音量,对setOnlineParam的封装
/// @param voiceVolume 默认0代表正常音量没有静音选项
-(void)setOnlineVoiceVolume:(float)voiceVolume;
/// 设置主语言类型,对setOnlineParam的封装
/// @param primaryLanguage 1:中文 2:英文 默认1
-(void)setOnlineVoiceLanguage:(int)primaryLanguage;
///在线编码格式,如无业务特殊需求不建议更改(默认"mp3",目前支持"mp3","wav","pcm",若更改为pcm不支持直接播放,对setOnlineParam的封装
-(void)setOnlineCodec:(NSString* _Nonnull) code;
/// 项目ID
/// @param projectId 默认0, 对setOnlineParam的封装
-(void)setOnlineProjectId:(int)projectId;
/// 是否开启时间戳功能默认为false,对setOnlineParam的封装
- (void)setOnlineEnableSubtitle:(Boolean)val;
/// 断句敏感阈值默认值为0,对setOnlineParam的封装
- (void)setOnlineSegmentRate:(int)val;
/// 控制合成音频的情感,仅支持多情感音色使用,对setOnlineParam的封装
- (void)setOnlineEmotionCategory:(NSString* _Nullable)val;
/// 控制合成音频情感程度,对setOnlineParam的封装
- (void)setOnlineEmotionIntensity:(int)val;
/// 设置自定义参数,可使用该方法控制请求时的参数
/// @param value为nil时将删除参数,否则会在请求中添加参数
- (void)setOnlineParam:(NSString* _Nonnull)key value:(NSObject* _Nullable)value;
/// 设置区域
/// @param region 默认为ap-shanghai,无特殊需求请勿更改
- (void)setOnlineRegion:(NSString *)region;
//设置是否输出日志
-(void)setEnableDebugLog:(BOOL)enableDebugLog;
/// timeoutIntervalForRequest ,0.5s - 30s 默认15000ms(15s)
/// @param timeout [2200,60000] 单位ms
- (void)setTimeoutIntervalForRequest:(int)timeout;
/// timeoutIntervalForResource 2.2s - 60s 默认30000ms(30s)
/// @param timeout [500,30000] 单位ms
-(void)setTimeoutIntervalForResource:(int) timeout;
/// Mix模式下出现网络错误后的检测间隔时间, 默认值5分钟
///注意:每次检测时将使用所入参的一句文本请求服务器,如果后端合成成功将会额外消耗该句字数的合成额度
/// @param checkNetworkIntervalTime 大于等于0 单位s, 等于0时持续检测直到成功
-(void)setCheckNetworkIntervalTime:(int) checkNetworkIntervalTime;
//以下为离线语音相关参数配置
//resourceDir 离线资源路径
-(void)setOfflineResourceDir:(NSString * _Nonnull) resourceDir;
//voiceSpeed [0.5,2.0]
-(void)setOfflineVoiceSpeed:(float) voiceSpeed;
//voiceType 离线音色名称,名称配置位于音色资源目录\voices\config.json 中可自行指定更多的音色demo中仅提供"pb"、"femalen"两种
-(void)setOfflineVoiceType:(NSString *_Nonnull) voiceType;
//voiceVolume >0
-(void)setOfflineVoiceVolume:(float) voiceVolume;
/// 配置离线TTS授权参数: 在线下载密钥(第一次激活设备需要联网)
/// @param licPk 密钥对应的licPk,请在腾讯云官网页面获取,或者由腾讯云商务线下下发
/// @param licKey 密钥对应的licKey,请在腾讯云官网页面获取,或者由腾讯云商务线下下发
/// @param secretId 腾讯云secretId 可能与在线模式不是同一个账号需要输入购买离线SDK对应的账号的secretId
/// @param secretKey 腾讯云 secretKey可能与在线模式不是同一个账号需要输入购买离线SDK对应的账号的secretKey
/// @param token token可为空如果使用sts临时证书鉴权secretId和secretKey均入参临时的同时需要入参对应的token
-(void)setOfflineAuthParamDoOnline:(NSString* _Nonnull)licPk
LicKey:(NSString* _Nonnull)licKey
SecretId:(NSString* _Nonnull)secretId
SecretKey:(NSString* _Nonnull)secretKey
Token:(NSString* _Nullable)token;
/// 配置离线TTS授权参数: 在线下载密钥(第一次激活设备需要联网)
/// @param licPk 密钥对应的licPk,请在腾讯云官网页面获取,或者由腾讯云商务线下下发
/// @param licKey 密钥对应的licKey,请在腾讯云官网页面获取,或者由腾讯云商务线下下发
/// @param secretId 腾讯云secretId 可能与在线模式不是同一个账号需要输入购买离线SDK对应的账号的secretId
/// @param secretKey 腾讯云 secretKey可能与在线模式不是同一个账号需要输入购买离线SDK对应的账号的secretKey
/// @param token token可为空如果使用sts临时证书鉴权secretId和secretKey均入参临时的同时需要入参对应的token
/// @param refreshAuth 是否强制联网刷新授权(NO:仅第一次联网激活下载授权文件; YES:联网刷新授权文件,无网络下将激活失败)
-(void)setOfflineAuthParamDoOnline:(NSString* _Nonnull)licPk
LicKey:(NSString* _Nonnull)licKey
SecretId:(NSString* _Nonnull)secretId
SecretKey:(NSString* _Nonnull)secretKey
Token:(NSString* _Nullable)token
RefreshAuth:(BOOL)refreshAuth;
/// 配置离线TTS授权参数: 直接传入密钥(第一次激活也不需要联网)
/// @param lic 授权密钥 ,请在腾讯云官网页面获取,或者由腾讯云商务线下下发
/// @param licPk 该密钥对应的licPk请在腾讯云官网页面获取或者由腾讯云商务线下下发
/// @param licSign 该密钥对应的licSign请在腾讯云官网页面获取或者由腾讯云商务线下下发
-(void)setOfflineAuthParamDoOffline:(NSString* _Nonnull)lic
LicPk:(NSString* _Nonnull)licPk
LicSign:(NSString* _Nonnull)licSign;
@end

View File

@ -0,0 +1,70 @@
//
// TtsError.h
// cloud-tts-sdk-ios
//
// Created by dgw on 2022/1/10.
//
#ifndef TtsError_h
#define TtsError_h
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, TtsErrorCode) {
TTS_ERROR_CODE_UNINITIALIZED = -100, //Engine uninitialized
TTS_ERROR_CODE_GENERATE_SIGN_FAIL = -101, //generate sign failed
TTS_ERROR_CODE_NETWORK_CONNECT_FAILED = 102, //network connect failed
TTS_ERROR_CODE_DECODE_FAIL = -103, //JSON Response Parsing Error
TTS_ERROR_CODE_SERVER_RESPONSE_ERROR = -104, //Server response error
TTS_ERROR_CODE_CANCEL_FAILURE = -106, //Cancel failureplease try again later
TTS_ERROR_CODE_OFFLINE_FAILURE = -107,//Offline synthesize failed, please check your text and VoiceType
TTS_ERROR_CODE_OFFLINE_INIT_FAILURE = -108,//Offline engine initialization failed, please check resource files
TTS_ERROR_CODE_OFFLINE_AUTH_FAILURE = -109,//Offline engine auth failure
TTS_ERROR_CODE_OFFLINE_TEXT_TOO_LONG = -110, //Offline synthesize failed,text too long,MAX <= 1024 byte
TTS_ERROR_CODE_OFFLINE_VOICE_AUTH_FAILURE = -111, //This voice not authorized, please change it
TTS_ERROR_CODE_OFFLINE_NOSUPPORT = -900 //This SDK only supports online mode
};
/// 后端返回的错误信息
@interface TtsServiceError : NSObject
@property (nonatomic, copy) NSString* _Nullable err_code;
@property (nonatomic, copy) NSString* _Nullable msg;
@property (nonatomic, copy) NSString* _Nullable respose;
+(instancetype _Nonnull)getTtsServiceError:(NSString *_Nullable)err_code ErrorMsg:(NSString* _Nullable)msg Respose:(NSString* _Nullable)respose;
@end
/// 错误信息类
@interface TtsError : NSObject
@property (nonatomic, assign) TtsErrorCode err_code; //错误码
@property (nonatomic, copy, nullable) NSString* msg; //错误信息
@property (nonatomic, copy, nullable) NSError * error; //系统抛出的错误信息(不一定有,可能为空)
@property (nonatomic, strong ,nullable) TtsServiceError * serviceError;//服务器返回的错误信息(不一定有,可能为空)
+(instancetype _Nonnull)getTtsError:(TtsErrorCode)err_code
TtsServiceError:(TtsServiceError* _Nullable)serviceError
NSErrorMsg:(NSError *_Nullable)error;
@end
#endif /* TtsError_h */

View File

@ -0,0 +1,207 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Headers/QCPlayerError.h</key>
<data>
GcBglYzaVwjgHfWYqQ+ZUSG3Uao=
</data>
<key>Headers/QCloudMediaPlayer.h</key>
<data>
cSuhgHCm42prRzNangBO/0w2qVA=
</data>
<key>Headers/QCloudMediaPlayer2.h</key>
<data>
WJT6QPmSe5jTGllxIhKljhOzgds=
</data>
<key>Headers/QCloudOfflineAuthInfo.h</key>
<data>
tolrRjjKQqWWjKFyUooedkLsIpc=
</data>
<key>Headers/QCloudPlayerDelegate.h</key>
<data>
EegDLAlAPciyhnxDnXB4oChNttQ=
</data>
<key>Headers/QCloudTTSEngine.h</key>
<data>
UQB6vhAlGndL5YeDl1ZEw3vyc2M=
</data>
<key>Headers/TtsError.h</key>
<data>
325otzQ/Cu8q0kGDUVU4SMibWZU=
</data>
<key>Info.plist</key>
<data>
2dDQqFGhXP8TDAtqPWpKsMxer5Y=
</data>
</dict>
<key>files2</key>
<dict>
<key>Headers/QCPlayerError.h</key>
<dict>
<key>hash</key>
<data>
GcBglYzaVwjgHfWYqQ+ZUSG3Uao=
</data>
<key>hash2</key>
<data>
B5vdIZRO7860RHiBmzU42oztYKV5/+TSI8nZImlfEHI=
</data>
</dict>
<key>Headers/QCloudMediaPlayer.h</key>
<dict>
<key>hash</key>
<data>
cSuhgHCm42prRzNangBO/0w2qVA=
</data>
<key>hash2</key>
<data>
dkOfBxGqa1bMsg4SkwH8H4qtwDba0not3AqU/goz0Mk=
</data>
</dict>
<key>Headers/QCloudMediaPlayer2.h</key>
<dict>
<key>hash</key>
<data>
WJT6QPmSe5jTGllxIhKljhOzgds=
</data>
<key>hash2</key>
<data>
aWSR35tX7sKARPhOHbJmpq4FYVNmlR7ST5aqDc4aLn4=
</data>
</dict>
<key>Headers/QCloudOfflineAuthInfo.h</key>
<dict>
<key>hash</key>
<data>
tolrRjjKQqWWjKFyUooedkLsIpc=
</data>
<key>hash2</key>
<data>
KLQptLzwtaJYFeFD49+ue23NLWqaZAyjWljo3VimNwg=
</data>
</dict>
<key>Headers/QCloudPlayerDelegate.h</key>
<dict>
<key>hash</key>
<data>
EegDLAlAPciyhnxDnXB4oChNttQ=
</data>
<key>hash2</key>
<data>
LrSEc56s6we6W+qm6wzJ4RFsfJXfvIM0MDzD1swuSqE=
</data>
</dict>
<key>Headers/QCloudTTSEngine.h</key>
<dict>
<key>hash</key>
<data>
UQB6vhAlGndL5YeDl1ZEw3vyc2M=
</data>
<key>hash2</key>
<data>
hsHHpz1PLxxiMLlxsy6ZqRbS7NlmoYybdFTUHQnrsXA=
</data>
</dict>
<key>Headers/TtsError.h</key>
<dict>
<key>hash</key>
<data>
325otzQ/Cu8q0kGDUVU4SMibWZU=
</data>
<key>hash2</key>
<data>
Jtc052fx72HaSNcpnKz0cL2f8VNTIYa1+DhDUcQX2v8=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^.*</key>
<true/>
<key>^.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^.*</key>
<true/>
<key>^.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>