Anonymous
Проверьте, действителен ли код для канала Merhod
Сообщение
Anonymous » 27 окт 2025, 17:19
импорт UIKit
импорт Flutter
импорт AVFoundation
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
private var ttsManager = TTSManager()
Код: Выделить всё
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
guard let controller = window?.rootViewController as? FlutterViewController else {
fatalError("RootViewController is not FlutterViewController")
}
let channel = FlutterMethodChannel(
name: "channelname/wifiworks",
binaryMessenger: controller.binaryMessenger
)
channel.setMethodCallHandler { [weak self] call, result in
guard let self = self else { return }
switch call.method {
case "CONVERT_TO_PCM":
self.ttsManager.convertToPCM(arguments: call.arguments, flutterResult: result)
case "disposeTts":
self.ttsManager.cleanup()
result(nil)
default:
result(FlutterMethodNotImplemented)
}
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
// MARK: - TTSManager (Optimized)
конечный класс TTSManager: NSObject, AVSpeechSynthesizerDelegate {
private let Queue = DispatchQueue(label: "com.app.ttsManager", qos: .userInitiated)
частный синтезатор var: AVSpeechSynthesizer?
частная переменная flutterResult: FlutterResult?
частная переменная audioFileURL: URL?
частная переменная isProcessing = false
Код: Выделить всё
// MARK: - Entry Point
func convertToPCM(arguments: Any?, flutterResult: @escaping FlutterResult) {
queue.async {
guard !self.isProcessing else {
flutterResult(FlutterError(code: "BUSY", message: "TTS already in progress", details: nil))
return
}
guard
let args = arguments as? [String: Any],
let text = args["text"] as? String, !text.isEmpty
else {
flutterResult(FlutterError(code: "INVALID_ARGUMENT", message: "Missing or empty text", details: nil))
return
}
self.isProcessing = true
self.flutterResult = flutterResult
self.startSynthesis(
text: text,
language: args["language"] as? String,
rate: args["rate"] as? Float ?? 1.0,
volume: args["volume"] as? Float ?? 1.0,
pitch: args["pitch"] as? Float ?? 1.0,
voiceName: args["voice"] as? String
)
}
}
// MARK: - Synthesis
private func startSynthesis(
text: String,
language: String?,
rate: Float,
volume: Float,
pitch: Float,
voiceName: String?
) {
cleanup() // reset any previous synthesis
let synthesizer = AVSpeechSynthesizer()
synthesizer.delegate = self
self.synthesizer = synthesizer
let utterance = AVSpeechUtterance(string: text)
utterance.voice = selectVoice(language: language, voiceName: voiceName)
utterance.pitchMultiplier = min(max(pitch, 0.5), 2.0)
utterance.volume = min(max(volume, 0.0), 1.0)
// Map [0.0, 2.0] rate to iOS range
let adjustedRate = AVSpeechUtteranceMinimumSpeechRate +
(min(rate, 2.0) * (AVSpeechUtteranceMaximumSpeechRate - AVSpeechUtteranceMinimumSpeechRate) / 2.0)
utterance.rate = adjustedRate
// Save synthesized speech directly to a temporary PCM WAV file
self.audioFileURL = FileManager.default.temporaryDirectory.appendingPathComponent("tts_output.wav")
do {
try AVAudioSession.sharedInstance().setCategory(.playback, options: [.mixWithOthers])
try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
} catch {
finishWithError(code: "AUDIO_SESSION", message: error.localizedDescription)
return
}
synthesizer.write(utterance) { [weak self] buffer in
guard let self = self else { return }
if let pcmBuffer = buffer as? AVAudioPCMBuffer {
self.appendPCMToFile(buffer: pcmBuffer)
} else if buffer is AVSpeechBufferCallbackEnd {
self.finishWritingToFile()
}
}
}
// MARK: - File Writing
private var fileWriter: AVAudioFile?
private func appendPCMToFile(buffer: AVAudioPCMBuffer) {
do {
if fileWriter == nil {
let format = buffer.format
fileWriter = try AVAudioFile(forWriting: audioFileURL!, settings: format.settings)
}
try fileWriter?.write(from: buffer)
} catch {
finishWithError(code: "FILE_WRITE", message: error.localizedDescription)
}
}
private func finishWritingToFile() {
fileWriter = nil
guard let url = audioFileURL else {
finishWithError(code: "FILE_MISSING", message: "No PCM data available")
return
}
do {
let data = try Data(contentsOf: url)
DispatchQueue.main.async {
self.flutterResult?(data)
self.flutterResult = nil
}
} catch {
finishWithError(code: "READ_ERROR", message: error.localizedDescription)
}
cleanup()
}
// MARK: - Voice Selection
private func selectVoice(language: String?, voiceName: String?) -> AVSpeechSynthesisVoice? {
if let voiceName = voiceName,
let voice = AVSpeechSynthesisVoice.speechVoices().first(where: { $0.identifier == voiceName }) {
return voice
}
if let language = language,
let voice = AVSpeechSynthesisVoice.speechVoices().first(where: { $0.language == language }) {
return voice
}
return AVSpeechSynthesisVoice(language: AVSpeechSynthesisVoice.currentLanguageCode())
}
// MARK: - Cleanup and Error Handling
func cleanup() {
synthesizer?.stopSpeaking(at: .immediate)
synthesizer?.delegate = nil
synthesizer = nil
fileWriter = nil
audioFileURL = nil
isProcessing = false
do {
try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
} catch {
NSLog("Failed to deactivate audio session: \(error)")
}
}
private func finishWithError(code: String, message: String) {
DispatchQueue.main.async {
self.flutterResult?(FlutterError(code: code, message: message, details: nil))
self.flutterResult = nil
}
cleanup()
}
// MARK: - AVSpeechSynthesizerDelegate
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
// Not needed — handled by `write` callback end
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancel utterance: AVSpeechUtterance) {
finishWithError(code: "CANCELLED", message: "TTS was cancelled")
}
Подробнее здесь:
https://stackoverflow.com/questions/798 ... od-channel
1761574757
Anonymous
импорт UIKit импорт Flutter импорт AVFoundation @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { private var ttsManager = TTSManager() [code]override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) guard let controller = window?.rootViewController as? FlutterViewController else { fatalError("RootViewController is not FlutterViewController") } let channel = FlutterMethodChannel( name: "channelname/wifiworks", binaryMessenger: controller.binaryMessenger ) channel.setMethodCallHandler { [weak self] call, result in guard let self = self else { return } switch call.method { case "CONVERT_TO_PCM": self.ttsManager.convertToPCM(arguments: call.arguments, flutterResult: result) case "disposeTts": self.ttsManager.cleanup() result(nil) default: result(FlutterMethodNotImplemented) } } return super.application(application, didFinishLaunchingWithOptions: launchOptions) } [/code] // MARK: - TTSManager (Optimized) конечный класс TTSManager: NSObject, AVSpeechSynthesizerDelegate { private let Queue = DispatchQueue(label: "com.app.ttsManager", qos: .userInitiated) частный синтезатор var: AVSpeechSynthesizer? частная переменная flutterResult: FlutterResult? частная переменная audioFileURL: URL? частная переменная isProcessing = false [code]// MARK: - Entry Point func convertToPCM(arguments: Any?, flutterResult: @escaping FlutterResult) { queue.async { guard !self.isProcessing else { flutterResult(FlutterError(code: "BUSY", message: "TTS already in progress", details: nil)) return } guard let args = arguments as? [String: Any], let text = args["text"] as? String, !text.isEmpty else { flutterResult(FlutterError(code: "INVALID_ARGUMENT", message: "Missing or empty text", details: nil)) return } self.isProcessing = true self.flutterResult = flutterResult self.startSynthesis( text: text, language: args["language"] as? String, rate: args["rate"] as? Float ?? 1.0, volume: args["volume"] as? Float ?? 1.0, pitch: args["pitch"] as? Float ?? 1.0, voiceName: args["voice"] as? String ) } } // MARK: - Synthesis private func startSynthesis( text: String, language: String?, rate: Float, volume: Float, pitch: Float, voiceName: String? ) { cleanup() // reset any previous synthesis let synthesizer = AVSpeechSynthesizer() synthesizer.delegate = self self.synthesizer = synthesizer let utterance = AVSpeechUtterance(string: text) utterance.voice = selectVoice(language: language, voiceName: voiceName) utterance.pitchMultiplier = min(max(pitch, 0.5), 2.0) utterance.volume = min(max(volume, 0.0), 1.0) // Map [0.0, 2.0] rate to iOS range let adjustedRate = AVSpeechUtteranceMinimumSpeechRate + (min(rate, 2.0) * (AVSpeechUtteranceMaximumSpeechRate - AVSpeechUtteranceMinimumSpeechRate) / 2.0) utterance.rate = adjustedRate // Save synthesized speech directly to a temporary PCM WAV file self.audioFileURL = FileManager.default.temporaryDirectory.appendingPathComponent("tts_output.wav") do { try AVAudioSession.sharedInstance().setCategory(.playback, options: [.mixWithOthers]) try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation) } catch { finishWithError(code: "AUDIO_SESSION", message: error.localizedDescription) return } synthesizer.write(utterance) { [weak self] buffer in guard let self = self else { return } if let pcmBuffer = buffer as? AVAudioPCMBuffer { self.appendPCMToFile(buffer: pcmBuffer) } else if buffer is AVSpeechBufferCallbackEnd { self.finishWritingToFile() } } } // MARK: - File Writing private var fileWriter: AVAudioFile? private func appendPCMToFile(buffer: AVAudioPCMBuffer) { do { if fileWriter == nil { let format = buffer.format fileWriter = try AVAudioFile(forWriting: audioFileURL!, settings: format.settings) } try fileWriter?.write(from: buffer) } catch { finishWithError(code: "FILE_WRITE", message: error.localizedDescription) } } private func finishWritingToFile() { fileWriter = nil guard let url = audioFileURL else { finishWithError(code: "FILE_MISSING", message: "No PCM data available") return } do { let data = try Data(contentsOf: url) DispatchQueue.main.async { self.flutterResult?(data) self.flutterResult = nil } } catch { finishWithError(code: "READ_ERROR", message: error.localizedDescription) } cleanup() } // MARK: - Voice Selection private func selectVoice(language: String?, voiceName: String?) -> AVSpeechSynthesisVoice? { if let voiceName = voiceName, let voice = AVSpeechSynthesisVoice.speechVoices().first(where: { $0.identifier == voiceName }) { return voice } if let language = language, let voice = AVSpeechSynthesisVoice.speechVoices().first(where: { $0.language == language }) { return voice } return AVSpeechSynthesisVoice(language: AVSpeechSynthesisVoice.currentLanguageCode()) } // MARK: - Cleanup and Error Handling func cleanup() { synthesizer?.stopSpeaking(at: .immediate) synthesizer?.delegate = nil synthesizer = nil fileWriter = nil audioFileURL = nil isProcessing = false do { try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation) } catch { NSLog("Failed to deactivate audio session: \(error)") } } private func finishWithError(code: String, message: String) { DispatchQueue.main.async { self.flutterResult?(FlutterError(code: code, message: message, details: nil)) self.flutterResult = nil } cleanup() } // MARK: - AVSpeechSynthesizerDelegate func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) { // Not needed — handled by `write` callback end } func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancel utterance: AVSpeechUtterance) { finishWithError(code: "CANCELLED", message: "TTS was cancelled") } [/code] Подробнее здесь: [url]https://stackoverflow.com/questions/79801917/check-if-the-code-is-valid-for-merhod-channel[/url]