Send Audio Byte Data through Web Service

As per my quiz game requirements, I want to send audio file at web server and get score from it.

I have used this library to record the audio and convert it into bytes data, all methods exist within this plugin.

Apart from this plugin, I was using this code to call the web service and get the score for recorded audio file.

public void GetScoreByAudio(string filePath)
    {

        byte[] fileContent = Recorder.Recorder.ConvertWAVtoByteArray(filePath);


        StartCoroutine(GetScoreByAudioEnumerator(fileContent));

        IEnumerator GetScoreByAudioEnumerator(byte[] fileContent)
        {
            WWWForm form = new WWWForm();
            form.AddField(TAG_AUDIO_WORD, "right");
            form.AddField(TAG_AUDIO_FILE_NAME, "recordertest.wav");

            //form.AddBinaryData(TAG_AUDIO_FILE_DATA, fileContent,"recordedaudio.wav", "multipart/form-data");
            //form.AddBinaryData(TAG_AUDIO_FILE_DATA, fileContent, "recordertest.wav", "audio/vnd.wave");
            form.AddBinaryData(TAG_AUDIO_FILE_DATA, fileContent);


            using (UnityWebRequest webRequest = UnityWebRequest.Post("https://gfbszns7ll.execute-api.eu-west-2.amazonaws.com/default/scoring-api-wrapper", form))
            {
                webRequest.SetRequestHeader(HEADER_AUTHORIZATION, DataStorage.RetrieveUserAccessToken());
                webRequest.SetRequestHeader("Content-Type", "audio/vnd.wave");

                // Request and wait for the desired page.
                yield return webRequest.SendWebRequest();

                switch (webRequest.result)
                {
                    case UnityWebRequest.Result.ConnectionError:
                    case UnityWebRequest.Result.DataProcessingError:
                        Debug.LogError("Error: " + webRequest.error);
                        break;
                    case UnityWebRequest.Result.ProtocolError:
                        Debug.LogError("HTTP Error: " + webRequest.error);
                        break;
                    case UnityWebRequest.Result.Success:
                        Debug.Log("Received: " + webRequest.downloadHandler.text);
                        break;
                }
            }
        }
    }

This kind of error, I am getting:

Only when I call within Unity editor, this kind of error is coming, with Postman everything working fine.
Please watch my recorded video for this:

Please suggest me changes that can work for me.

That’s excellent. Sounds like you need charles or wireshark or some kind of proxy to figure out how the two requests differ, and then to adjust so they comply.

Networking, UnityWebRequest, WWW, Postman, curl, WebAPI, etc:

https://discussions.unity.com/t/831681/2

https://discussions.unity.com/t/837672/2

And setting up a proxy can be very helpful too, in order to compare traffic:

https://support.unity.com/hc/en-us/articles/115002917683-Using-Charles-Proxy-with-Unity

It says it’s an internal server error. Does the server that’s hosting the service have any logs that you can look at?

Right, HTTP response code 5xx indicates a server side issue. Of course it’s possible that the client may send the wrong data so the server can’t handle the request properly. However in any case, without knowing what the server expects or what may go wrong on the server side we can only guess. What we can tell is that the server is having an issue.

Apart from that we can also tell that this line makes no sense:

webRequest.SetRequestHeader("Content-Type", "audio/vnd.wave");

You’re sending an html form to your server. So it contains url encoded fields as well as binary data. As such the content type of the request must be “multipart/form-data” and Unity does handle this for you. So messing with the content type header makes no sense. This header also contains the boundary string that is required to properly decode the multipart sections. So you should remove that line.

If you want to specify the content type of your file upload, you have to pass the mime type to the “AddBinaryData” call. It has several overloads and there’s one that takes an additional file name and mime type.

Something like that:

form.AddBinaryData(TAG_AUDIO_FILE_DATA, fileContent, null, "audio/vnd.wave");

Note: When you don’t supply a file name (like I did here), Unity will by default use the field name and add a “.dat” at the end. Just in case the file name has any relevance.

Thank you sir for your reply :slight_smile:
Hello sir, if you remember 3 to 4 days ago, you have helped me to send JSON string to web server and at that time, I was getting the same error code, 500 internal server error.
So at that time, we changed in the code little bit and its started working and in this case also within Postman it was working.
https://discussions.unity.com/t/895522/5

This time, I want to send wave file data at web server through programmer web service, as like I have shown in the recorded video.
As per my thinking, I was sending wrong data to web server so it can’t able to process my web service request successfully.

After your above suggestion, I have changed my code something like this:

public void GetScoreByAudio(string filePath)
    {

        byte[] fileContent = Recorder.Recorder.ConvertWAVtoByteArray(filePath);


        StartCoroutine(GetScoreByAudioEnumerator(fileContent));

        IEnumerator GetScoreByAudioEnumerator(byte[] fileContent)
        {
            WWWForm form = new WWWForm();
            form.AddField(TAG_AUDIO_WORD, "right");
            form.AddField(TAG_AUDIO_FILE_NAME, "recordertest.wav");

            form.AddBinaryData(TAG_AUDIO_FILE_DATA, fileContent, "recordertest.wav", "audio/vnd.wave");
            //form.AddBinaryData(TAG_AUDIO_FILE_DATA, fileContent);


            using (UnityWebRequest webRequest = UnityWebRequest.Post("https://gfbszns7ll.execute-api.eu-west-2.amazonaws.com/default/scoring-api-wrapper", form))
            {
                webRequest.SetRequestHeader(HEADER_AUTHORIZATION, DataStorage.RetrieveUserAccessToken());
                //webRequest.SetRequestHeader("Content-Type", "audio/vnd.wave");

                // Request and wait for the desired page.
                yield return webRequest.SendWebRequest();

                switch (webRequest.result)
                {
                    case UnityWebRequest.Result.ConnectionError:
                    case UnityWebRequest.Result.DataProcessingError:
                        Debug.LogError("Error: " + webRequest.error);
                        break;
                    case UnityWebRequest.Result.ProtocolError:
                        Debug.LogError("HTTP Error: " + webRequest.error);
                        break;
                    case UnityWebRequest.Result.Success:
                        Debug.Log("Received: " + webRequest.downloadHandler.text);
                        break;
                }
            }
        }

But unluckily the result was same.
Let me know PUT method, I can use or not.
This kind of structure, I got from the web developer:
8487929--1129091--audio score - api structure.png

Please give me next suggestions so I can try.

This description doesn’t talk about sending a html form to the server but using get request parameters in the url itself. In that case you have to remove your whole “form” stuff and use something like this instead:

string parameters = "?"+TAG_AUDIO_WORD+"=" + "right"+"&" + TAG_AUDIO_FILE_NAME+"="+"recordertest.wav";
string url = GameConstants.HOOPSENGLISH_BASE_URL + GameConstants.WEB_UPDATE_USER_PROFILE_DATA_URI + parameters;

using (UnityWebRequest webRequest = UnityWebRequest.Put(url, fileContent))
{
    webRequest.method = UnityWebRequest.kHttpVerbPOST;
    webRequest.SetRequestHeader("Content-Type", "audio/vnd.wave");
    // [ ... ]

This is a normal upload without an html form. So we directly upload the audio data and the parameters are part of the url. Keep in mind that the currently hardcoded values “right” and “recordertest.wav” should be url encoded. If they are constant and do not change it should work that way. However if those should be provided externally those values should be put through UnityWebRequest.EscapeURL.

ps: If the “query parameters” confused you, have a look at the syntax of an URL.

1 Like

Oh my god, Oh my god, Sir :slight_smile:
Its working and I am dancing…

public void GetScoreByAudio(string filePath)
    {

        byte[] fileContent = Recorder.Recorder.ConvertWAVtoByteArray(filePath);

        string parameters = "?" + TAG_AUDIO_WORD + "=" + "right" + "&" + TAG_AUDIO_FILE_NAME + "=" + "recordertest.wav";

        StartCoroutine(GetScoreByAudioEnumerator(fileContent));

        IEnumerator GetScoreByAudioEnumerator(byte[] fileContent)
        {

            using (UnityWebRequest webRequest = UnityWebRequest.Put("https://gfbszns7ll.execute-api.eu-west-2.amazonaws.com/default/scoring-api-wrapper" + parameters, fileContent))
            {
                webRequest.method = UnityWebRequest.kHttpVerbPOST;
                webRequest.SetRequestHeader(HEADER_AUTHORIZATION, DataStorage.RetrieveUserAccessToken());
                webRequest.SetRequestHeader(HEADER_CONTENT_TYPE, "audio/vnd.wave");
                //webRequest.SetRequestHeader(HEADER_ACCEPT, "application/json");

                // Request and wait for the desired page.
                yield return webRequest.SendWebRequest();

                switch (webRequest.result)
                {
                    case UnityWebRequest.Result.ConnectionError:
                    case UnityWebRequest.Result.DataProcessingError:
                        Debug.LogError("Error: " + webRequest.error);
                        break;
                    case UnityWebRequest.Result.ProtocolError:
                        Debug.LogError("HTTP Error: " + webRequest.error);
                        break;
                    case UnityWebRequest.Result.Success:
                        Debug.Log("Received: " + webRequest.downloadHandler.text);
                        break;
                }
            }
        }
    }

One last question, two parameters I am sending with the web request, “file name” and “word”.
From these, “file name” I will not change but “word” value I have to change rather than static “right” word.
If I just place string variable over their rather than static “right” word then will it work?

Why I required to use UnityWebRequest.EscapeURL? that I can’t able to understand.

1 Like

Parameters in an URL must not contain any characters that have a special meaning inside ah URL. That includes things like /, &, %, :, ... and a few others. If the parameter value need to include any such special character, those characters need to be URL encoded. This is also true for the space character. A space has to be escaped to either %20 (which is hexadecimal for 32 which is a space in ASCII) or a + character.

As I said, if you know the values you’re going to use are safe to be used directly, there’s no need to escape them. However if the values can change or come from user input, you should always escape them.

1 Like

@Bunny83 I don’t know why but again I started getting issue of:
HTTP Error: HTTP/1.1 500 Internal Server Error

Before it worked and now its completely stopped working, I didn’t made any changes after this.
I am attached the whole script so you can understand better way.

Please give some suggestion based on this.

8552696–1143575–AudioQuestion.cs (11.3 KB)

Same approach still applies.

Networking, UnityWebRequest, WWW, Postman, curl, WebAPI, etc:

https://discussions.unity.com/t/831681/2

https://discussions.unity.com/t/837672/2

And setting up a proxy can be very helpful too, in order to compare traffic:

https://support.unity.com/hc/en-us/articles/115002917683-Using-Charles-Proxy-with-Unity

1 Like