iOS端如何将Text-to-Speech生成的语音存储为本地音频文件
问题原因及解决方法
1. WriteUtterance回调不触发问题
同一个AVSpeechUtterance实例不可同时调用SpeakUtterance和WriteUtterance,仅保留WriteUtterance即可解决回调不触发问题,你已经完成了这一步修正。
2. AVAudioFile初始化失败问题
该报错有三个常见触发原因,按优先级排查即可:
- 存储目录不存在:你使用的
TTS子目录在临时目录下不会自动生成,创建文件前需要先判断并创建目录// 先创建TTS目录 string ttsFolderPath = Path.Combine(Path.GetTempPath(), "TTS"); if (!Directory.Exists(ttsFolderPath)) { Directory.CreateDirectory(ttsFolderPath); } string filePath = Path.Combine(ttsFolderPath, "1.caf"); NSUrl fileUrl = NSUrl.FromFilename(filePath); - 音频参数硬编码不匹配:不要强制指定
commonFormat和interleaved参数,直接使用buffer自带的格式配置即可,修改构造方法调用:
如果确实需要指定存储格式,先打印// 去掉硬编码的格式参数,直接用buffer自带的settings output = new AVAudioFile(fileUrl, pcmBuffer.Format.Settings, out error);pcmBuffer.Format的具体参数,确认你指定的格式与buffer原生格式兼容后再调整。 - 变量生命周期问题:你当前的代码将
AVAudioFile实例定义在回调方法内部,每次回调都会重新初始化覆盖之前的文件,同时AVSpeechSynthesizer作为局部变量会被GC提前回收导致回调中断,需要将这两个实例提升为类的成员变量,等所有音频buffer写入完成后再释放。
完整修正代码示例
// 定义为类成员变量,避免被GC回收 private AVSpeechSynthesizer _synthesizer; private AVAudioFile _outputFile; public async Task Amethod(string[] _text_and_position) { string[] text_and_position = (string[])_text_and_position; double tts_starting_position = Convert.ToDouble(text_and_position[0]); string text = text_and_position[1]; _synthesizer = new AVSpeechSynthesizer(); var su = new AVSpeechUtterance(text) { Rate = 0.5f, Volume = 1.6f, PitchMultiplier = 1.4f, Voice = AVSpeechSynthesisVoice.FromLanguage("en-us") }; Action<AVAudioBuffer> bufferHandler = new Action<AVAudioBuffer>(HandleBuffer); try { _synthesizer.WriteUtterance(su, bufferHandler); } catch (Exception error) { new UIAlertView("Error", error.ToString(), null, "OK", null).Show(); } } public async void HandleBuffer(AVAudioBuffer _buffer) { try { var pcmBuffer = (AVAudioPcmBuffer)_buffer; NSError error; if (pcmBuffer.FrameLength == 0) { // 写入完成,释放文件实例 _outputFile?.Dispose(); _outputFile = null; return; } if (_outputFile == null) { // 先创建目录 string ttsFolderPath = Path.Combine(Path.GetTempPath(), "TTS"); if (!Directory.Exists(ttsFolderPath)) { Directory.CreateDirectory(ttsFolderPath); } string filePath = Path.Combine(ttsFolderPath, "1.caf"); NSUrl fileUrl = NSUrl.FromFilename(filePath); // 使用buffer自带格式初始化文件 _outputFile = new AVAudioFile(fileUrl, pcmBuffer.Format.Settings, out error); if (error != null) { new UIAlertView("File Error", error.LocalizedDescription, null, "OK", null).Show(); return; } } _outputFile.WriteFromBuffer(pcmBuffer, out error); if (error != null) { new UIAlertView("Write Error", error.LocalizedDescription, null, "OK", null).Show(); } } catch (Exception error) { new UIAlertView("Error", error.ToString(), null, "OK", null).Show(); } }
替代方案
如果仍无法满足需求,可以使用iOS自带的AVAudioRecorder在TTS播放时同步录制音频,或者接入第三方TTS服务直接返回音频文件流,再和视频合并。
内容的提问来源于stack exchange,提问作者Jaime Santos
相关产品推荐
相关产品推荐

