You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 02:06:07