---
# System prepended metadata

title: Google API 研究
tags: [程式]

---

# Google API 研究
###### tags: `程式`

* [Package com.google.cloud.speech.v1 (2.5.4](https://cloud.google.com/java/docs/reference/google-cloud-speech/latest/com.google.cloud.speech.v1)
* [How to Use RESTful APIs In Unity [Unity Tutorial]
](https://www.youtube.com/watch?v=XIbZDz_p6vE)
* https://github.com/GlitchEnzo/NuGetForUnity
* [Google.Cloud.Speech.V1](https://www.nuget.org/packages/Google.Cloud.Speech.V1/)
* really???

結果只要用IDE(ex:Visual Studio)就可以使用NuGet了Orz，虧我翻了老半天...
pruss ( 程式 )
要在 Unity 上裝Nuget的東西 可以在IDE 裡面下Nuget 指令 IDE 會把dll 下載到Packages 資料夾 再把dll 丟到專案目錄就可以了
感謝pruss大大Orz
![](https://i.imgur.com/iDqQJSU.png)
原來要是版本有問題dll檔會自己跳出來說要裝哪個版本啊Orz
![](https://i.imgur.com/UZMGt8E.png)
[How to: Reference a strong-named assembly](https://learn.microsoft.com/en-us/dotnet/standard/assembly/reference-strong-named)
[Manage packages with the Visual Studio Package Manager Console (PowerShell)](https://learn.microsoft.com/en-us/nuget/consume-packages/install-use-packages-powershell)

## 語音辨識與語音訴說版本紀錄
### GoogleCloudSpeechToText版本稍微紀錄
* Google.Cloud.Speech 
* 

### 自己架的版本
* Google.Cloud.TextToSpeech

### TextToSpeech相關資料
* [Create voice audio files](https://cloud.google.com/text-to-speech/docs/create-audio)

轉語音參考程式碼
```
/// <summary>
/// This method is called once by the Unity coroutine once the speech is successfully synthesized.
/// It will then attempt to play that audio file.
/// Note that the playback will fail if the output audio format is not pcm encoded.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="args">The <see cref="GenericEventArgs{Stream}"/> instance containing the event data.</param>
//private void PlayAudio(object sender, GenericEventArgs<Stream> args)
private void PlayAudio(Stream audioStream)
{
    Debug.Log("Playing audio stream");

    // Play the audio using Unity AudioSource, allowing us to benefit from effects,
    // spatialization, mixing, etc.

    // Get the size of the original stream
    var size = audioStream.Length;

    // Don't playback if the stream is empty
    if (size > 0)
    {
        try
        {
            Debug.Log($"Creating new byte array of size {size}");
            // Create buffer
            byte[] buffer = new byte[size];

            Debug.Log($"Reading stream to the end and putting in bytes array.");
            buffer = ReadToEnd(audioStream);

            // Convert raw WAV data into Unity audio data
            Debug.Log($"Converting raw WAV data of size {buffer.Length} into Unity audio data.");
            int sampleCount = 0;
            int frequency = 0;
            var unityData = AudioWithHeaderToUnityAudio(buffer, out sampleCount, out frequency);

            // Convert data to a Unity audio clip
            Debug.Log($"Converting audio data of size {unityData.Length} to Unity audio clip with {sampleCount} samples at frequency {frequency}.");
            var clip = ToClip("Speech", unityData, sampleCount, frequency);

            // Set the source on the audio clip
            audioSource.clip = clip;

            Debug.Log($"Trigger playback of audio clip on AudioSource.");
            // Play audio
            audioSource.Play();
        }
        catch (Exception ex)
        {
            Debug.Log("An error occurred during audio stream conversion and playback." 
                       + Environment.NewLine + ex.Message);
        }
    }
}
    /// <summary>
    /// Reads a stream from beginning to end, returning an array of bytes
    /// </summary>
    /// <param name="stream"></param>
    /// <returns></returns>
    public static byte[] ReadToEnd(Stream stream)
    {
        long originalPosition = 0;

        if (stream.CanSeek)
        {
            originalPosition = stream.Position;
            stream.Position = 0;
        }

        try
        {
            byte[] readBuffer = new byte[4096];

            int totalBytesRead = 0;
            int bytesRead;

            while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
            {
                totalBytesRead += bytesRead;

                if (totalBytesRead == readBuffer.Length)
                {
                    int nextByte = stream.ReadByte();
                    if (nextByte != -1)
                    {
                        byte[] temp = new byte[readBuffer.Length * 2];
                        Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
                        Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
                        readBuffer = temp;
                        totalBytesRead++;
                    }
                }
            }

            byte[] buffer = readBuffer;
            if (readBuffer.Length != totalBytesRead)
            {
                buffer = new byte[totalBytesRead];
                Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
            }
            return buffer;
        }
        finally
        {
            if (stream.CanSeek)
            {
                stream.Position = originalPosition;
            }
        }
    }

    /// <summary>
    /// Converts two bytes to one float in the range -1 to 1.
    /// </summary>
    /// <param name="firstByte">The first byte.</param>
    /// <param name="secondByte"> The second byte.</param>
    /// <returns>The converted float.</returns>
    private static float BytesToFloat(byte firstByte, byte secondByte)
    {
        // Convert two bytes to one short (little endian)
        short s = (short)((secondByte << 8) | firstByte);

        // Convert to range from -1 to (just below) 1
        return s / 32768.0F;
    }

    /// <summary>
    /// Converts an array of bytes to an integer.
    /// </summary>
    /// <param name="bytes"> The byte array.</param>
    /// <param name="offset"> An offset to read from.</param>
    /// <returns>The converted int.</returns>
    private static int BytesToInt(byte[] bytes, int offset = 0)
    {
        int value = 0;
        for (int i = 0; i < 4; i++)
        {
            value |= ((int)bytes[offset + i]) << (i * 8);
        }
        return value;
    }

    /// <summary>
    /// Dynamically creates an <see cref="AudioClip"/> that represents raw Unity audio data.
    /// </summary>
    /// <param name="name"> The name of the dynamically generated clip.</param>
    /// <param name="audioData">Raw Unity audio data.</param>
    /// <param name="sampleCount">The number of samples in the audio data.</param>
    /// <param name="frequency">The frequency of the audio data.</param>
    /// <returns>The <see cref="AudioClip"/>.</returns>
    private static AudioClip ToClip(string name, float[] audioData, int sampleCount, int frequency)
    {
        var clip = AudioClip.Create(name, sampleCount, 1, frequency, false);
        clip.SetData(audioData, 0);
        return clip;
    }

    /// <summary>
    /// Converts raw WAV data into Unity formatted audio data.
    /// </summary>
    /// <param name="wavAudio">The raw WAV data.</param>
    /// <param name="sampleCount">The number of samples in the audio data.</param>
    /// <param name="frequency">The frequency of the audio data.</param>
    /// <returns>The Unity formatted audio data. </returns>
    private static float[] AudioWithHeaderToUnityAudio(byte[] wavAudio, out int sampleCount, out int frequency)
    {
        // Determine if mono or stereo
        int channelCount = wavAudio[22];  // Speech audio data is always mono but read actual header value for processing
        Debug.Log($"Audio data has {channelCount} channel(s).");

        // Get the frequency
        frequency = BytesToInt(wavAudio, 24);
        Debug.Log($"Audio data frequency is {frequency}.");

        // Get past all the other sub chunks to get to the data subchunk:
        int pos = 12; // First subchunk ID from 12 to 16

        // Keep iterating until we find the data chunk (i.e. 64 61 74 61 ...... (i.e. 100 97 116 97 in decimal))
        while (!(wavAudio[pos] == 100 && wavAudio[pos + 1] == 97 && wavAudio[pos + 2] == 116 && wavAudio[pos + 3] == 97))
        {
            pos += 4;
            int chunkSize = wavAudio[pos] + wavAudio[pos + 1] * 256 + wavAudio[pos + 2] * 65536 + wavAudio[pos + 3] * 16777216;
            pos += 4 + chunkSize;
        }
        pos += 8;

        // Pos is now positioned to start of actual sound data.
        sampleCount = (wavAudio.Length - pos) / 2;  // 2 bytes per sample (16 bit sound mono)
        if (channelCount == 2) { sampleCount /= 2; }  // 4 bytes per sample (16 bit stereo)
        Debug.Log($"Audio data contains {sampleCount} samples. Starting conversion");

        // Allocate memory (supporting left channel only)
        var unityData = new float[sampleCount];

        try
        {
            // Write to double array/s:
            int i = 0;
            while (pos < wavAudio.Length)
            {
                unityData[i] = BytesToFloat(wavAudio[pos], wavAudio[pos + 1]);
                pos += 2;
                if (channelCount == 2)
                {
                    pos += 2;
                }
                i++;
            }
        }
        catch (Exception ex)
        {
            Debug.Log($"Error occurred converting audio data to float array of size {wavAudio.Length} at position {pos}.");
        }

        return unityData;
    }

    /// <summary>
    /// Converts raw WAV data into Unity formatted audio data.
    /// </summary>
    /// <param name="wavAudio">The raw WAV data.</param>
    /// <param name="sampleCount">The number of samples in the audio data.</param>
    /// <param name="frequency">The frequency of the audio data.</param>
    /// <returns>The Unity formatted audio data. </returns>
    private static float[] FixedRAWAudioToUnityAudio(byte[] wavAudio, int channelCount, int resolution, out int sampleCount)
    {
        // Pos is now positioned to start of actual sound data.
        int bytesPerSample = resolution / 8; // e.g. 2 bytes per sample (16 bit sound mono)
        sampleCount = wavAudio.Length / bytesPerSample;
        if (channelCount == 2) { sampleCount /= 2; }  // 4 bytes per sample (16 bit stereo)
        Debug.Log($"Audio data contains {sampleCount} samples. Starting conversion");

        // Allocate memory (supporting left channel only)
        var unityData = new float[sampleCount];

        int pos = 0;
        try
        {
            // Write to double array/s:
            int i = 0;
            while (pos < wavAudio.Length)
            {
                unityData[i] = BytesToFloat(wavAudio[pos], wavAudio[pos + 1]);
                pos += 2;
                if (channelCount == 2)
                {
                    pos += 2;
                }
                i++;
            }
        }
        catch (Exception ex)
        {
            Debug.Log($"Error occurred converting audio data to float array of size {wavAudio.Length} at position {pos}.");
        }

        return unityData;
    }
```
### SpeechToText相關紀錄
```
    //如果接到例外會強制停止
    catch (TaskCanceledException)
    {

```

### Google cloud相關設定
* [Service accounts](https://cloud.google.com/iam/docs/service-accounts)
    * 管理權限的東西
* [Install the Google Cloud CLI](https://cloud.google.com/sdk/docs/install-sdk)
    * 去學校電腦測試時應該也要再裝一次...好吧不需要網路就可以呼叫此Cloud shell了Orz
* Google Cloud
    * Cloud shell
* [Apply for monthly invoiced billing](https://cloud.google.com/billing/docs/how-to/invoiced-billing)

```
        while (!(wavAudio[pos] == 100 && wavAudio[pos + 1] == 97 && wavAudio[pos + 2] == 116 && wavAudio[pos + 3] == 97))
        {
            pos += 4;
            int chunkSize = wavAudio[pos] + wavAudio[pos + 1] * 256 + wavAudio[pos + 2] * 65536 + wavAudio[pos + 3] * 16777216;
            pos += 4 + chunkSize;
        }
        pos += 8;
```

* ByteString轉成Byte在經過一連串轉成Unity可以播的資料

## 數字轉換
* [Google cloud speech to text - How to get numbers in digit
](https://stackoverflow.com/questions/66206056/google-cloud-speech-to-text-how-to-get-numbers-in-digit)
* [Supproted class tokens](https://cloud.google.com/speech-to-text/docs/class-tokens)

## 雜項
* [Naudio](https://github.com/naudio/NAudio)
    * 用來轉換MP3格式的API
* [Send a recognition request with model adaptation](https://cloud.google.com/speech-to-text/docs/adaptation)
* https://cloud.google.com/speech-to-text/docs/reference/rest/v1p1beta1/projects.locations.customClasses
* https://cloud.google.com/speech-to-text/docs/class-tokens
* https://stackoverflow.com/questions/66206056/google-cloud-speech-to-text-how-to-get-numbers-in-digit
* https://groups.google.com/g/cloud-speech-discuss/c/tocHI0uQ2rE?pli=1
* 更進階的功能可能要用Python寫...Orz