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

Unity集成Google Cloud Speech-to-Text API仅返回指定字段的解决方法

Unity调用Google Cloud Speech-to-Text API无识别结果问题排查与解决

问题现象

调用https://speech.googleapis.com/v1/speech:recognize?key={API_KEY}接口后,仅返回requestId和totalBilledTime,缺失results、speechAdaptationInfo等核心识别结果字段。

排查方向与解决办法

1. 音频格式与编码验证

Google Speech-to-Text对LINEAR16编码的音频有严格要求:

  • 强制转为单声道:如果原AudioClip是立体声,会直接导致识别失败。修改ConvertAudioClipToBase64方法,添加单声道转换逻辑:
    // 转为单声道
    float[] monoSamples = new float[clip.samples];
    if (clip.channels == 2)
    {
        for (int i = 0; i < clip.samples; i++)
        {
            monoSamples[i] = (samples[i * 2] + samples[i * 2 + 1]) / 2f;
        }
    }
    else
    {
        monoSamples = samples;
    }
    // 后续用monoSamples转换为PCM16
    
  • 严格匹配采样率:代码中设置的是16000Hz,必须保证原AudioClip的采样率也是16000Hz,否则需先对音频进行重采样处理。

2. JSON序列化问题

Unity自带的JsonUtility存在序列化限制,可能导致JSON字段名或结构不符合API要求:

  • 检查JsonUtility.ToJson(jsonRequest)的输出,确认config、encoding等字段为小写(Google API要求全小写字段名)。如果序列化结果异常,改用Newtonsoft.Json(可通过Unity Package Manager导入):
    string json = JsonConvert.SerializeObject(jsonRequest);
    

3. 音频数据完整性优化

  • 避免本地文件中转:原代码先写入output.pcm再读取,可能触发IO错误。直接在内存中处理音频数据:
    // 替换文件读写部分
    using (MemoryStream ms = new MemoryStream())
    using (BinaryWriter bw = new BinaryWriter(ms))
    {
        foreach (short sample in pcmData)
        {
            bw.Write(sample);
        }
        byte[] audioData = ms.ToArray();
        string base64String = Convert.ToBase64String(audioData);
        return base64String;
    }
    
  • 验证Base64有效性:将生成的Base64字符串解码为PCM文件,用Audacity等工具打开,确认音频可正常播放。

4. API请求配置补充

  • 确认使用正确的接口:当前地址为同步识别接口,仅支持1分钟以内的音频;若音频更长,需改用异步识别接口。
  • 可在config中添加enableAutomaticPunctuation: true等参数,提升识别准确率,但核心前提是音频格式符合要求。

修正后的核心代码示例

音频转换优化

string ConvertAudioClipToBase64(AudioClip clip)
{
    // 校验采样率
    if (clip.frequency != 16000)
    {
        Debug.LogError("AudioClip采样率必须为16000Hz");
        return string.Empty;
    }

    float[] samples = new float[clip.samples * clip.channels];
    clip.GetData(samples, 0);

    // 转为单声道
    float[] monoSamples = new float[clip.samples];
    if (clip.channels == 2)
    {
        for (int i = 0; i < clip.samples; i++)
        {
            monoSamples[i] = (samples[i * 2] + samples[i * 2 + 1]) / 2f;
        }
    }
    else
    {
        monoSamples = samples;
    }

    short[] pcmData = ConvertToPCM16(monoSamples);

    // 内存中处理音频数据
    using (MemoryStream ms = new MemoryStream())
    using (BinaryWriter bw = new BinaryWriter(ms))
    {
        foreach (short sample in pcmData)
        {
            bw.Write(sample);
        }
        byte[] audioData = ms.ToArray();
        return Convert.ToBase64String(audioData);
    }
}

private short[] ConvertToPCM16(float[] samples)
{
    short[] pcmData = new short[samples.Length];
    for (int i = 0; i < samples.Length; i++)
    {
        pcmData[i] = (short)(Mathf.Clamp(samples[i], -1f, 1f) * short.MaxValue);
    }
    return pcmData;
}

请求序列化优化(使用Newtonsoft.Json)

// 需先导入Newtonsoft.Json包
var jsonRequest = new SpeechToTextRequest
{
    config = new AudioConfig { encoding = "LINEAR16", sampleRateHertz = 16000, languageCode = "ja-JP"},
    audio = new SpeechToTextInput { content = base64Audio}
};

string json = JsonConvert.SerializeObject(jsonRequest);

var request = new UnityWebRequest(url, "POST")
{
    uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(json)),
    downloadHandler = new DownloadHandlerBuffer()
};
request.SetRequestHeader("Content-Type", "application/json");

await request.SendWebRequest().ToUniTask();

if (request.result == UnityWebRequest.Result.Success)
{
    var response = request.downloadHandler.text;
    // 反序列化为响应类查看完整结果
    var speechResponse = JsonConvert.DeserializeObject<SpeechToTextResponse>(response);
}
else
{
    throw new Exception($"Error: {request.error}");
}

// 响应类定义
[Serializable]
private class SpeechToTextResponse
{
    public List<SpeechResult> results;
    public string totalBilledTime;
    public SpeechAdaptationInfo speechAdaptationInfo;
    public string requestId;
}

[Serializable]
private class SpeechResult
{
    // 根据API文档添加对应字段,如alternatives等
}

[Serializable]
private class SpeechAdaptationInfo
{
    // 对应API返回的适配信息字段
}

内容的提问来源于stack exchange,提问作者ばねっと

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 02:23:11