0

This one's being troubling me for a couple of days now. I'm using Unity 3D coding in C# and the desired result is to hit a button, save a short audio recording of my voice locally then send it through to an API online. It works fine on the laptop in Unity just not when I save to mobile so I'm thinking it's something to do with the persistent data path route possibly.

I have a script which records and then calls a savwav.cs file to store the sample.wav file. Just can't get the thing working on my a mobile.

If someone could point me in the right direction it would be a big help. Maybe I need to save in binary format or something like that?

I'm using a modified version of a file I was grateful to find on git to save the wav and after changing it slightly to suit my specific needs it all works great, till the build to device. No errors just doesn't seem to work. The initial save attempt is in the update function : SavWav.Save("sample", commandClip);

The relevant code for the initial file is :

    // Update is called once per frame
void Update () {
    if (pressedButton == true) {
        pressedButton = false;
        if (isRecording) {
            myResultBox.text = "Listening for command";
            commandClip = Microphone.Start (null, false, 10, samplerate);  //Start recording (rewriting older recordings)
        }


        if (!isRecording) {
            myResultBox.text = null;
            myResultBox.text = "Thinking";
            // Save the audio file
            Microphone.End (null);
            SavWav.Save("sample", commandClip);

            // At this point, we can delete the existing audio clip
            commandClip = null;

            token = "my token code";

            //Start a coroutine called "WaitForRequest" with that WWW variable passed in as an argument
            string witAiResponse = GetJSONText (Application.persistentDataPath + "/" +"sample.wav");
            print (witAiResponse);
            Handle (witAiResponse);
        }
    }

}

string GetJSONText(string file) {

    // get the file w/ FileStream

    FileStream filestream = new FileStream (file, FileMode.Open, FileAccess.Read);
    BinaryReader filereader = new BinaryReader (filestream);
    byte[] BA_AudioFile = filereader.ReadBytes ((Int32)filestream.Length);
    filestream.Close ();
    filereader.Close ();

    // create an HttpWebRequest
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://api.wit.ai/speech?v=20171127");

    request.Method = "POST";
    request.Headers ["Authorization"] = "Bearer " + token;
    request.ContentType = "audio/wav";
    request.ContentLength = BA_AudioFile.Length;
    request.GetRequestStream ().Write (BA_AudioFile, 0, BA_AudioFile.Length);

    // Process the wit.ai response
    try
    {
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        if (response.StatusCode == HttpStatusCode.OK)
        {
            print("Http went through ok");
            StreamReader response_stream = new StreamReader(response.GetResponseStream());
            return response_stream.ReadToEnd();
        }
        else
        {
            return "Error: " + response.StatusCode.ToString();
            return "HTTP ERROR";
        }
    }
    catch (Exception ex)
    {
        return "Error: " + ex.Message;
        return "HTTP ERROR";
    }       
}

The save file to wav code from git then modified slightly to follow the persistent data path route is :

using System;
using System.IO;
using UnityEngine;
using System.Collections.Generic;
using System.Threading;

public static class SavWav{

    const int HEADER_SIZE = 44;
    struct ClipData{

    public  int samples;
    public  int channels;
    public float[] samplesData;

}


public static bool Save(string filename, AudioClip clip) {
    if (!filename.ToLower().EndsWith(".wav")) {
        filename += ".wav";
    }

    var filepath = Path.Combine(Application.persistentDataPath+ "/", filename);

    Debug.Log(filepath);

    // Make sure directory exists if user is saving to sub dir.
    Directory.CreateDirectory(Path.GetDirectoryName(filepath));

    ClipData clipdata = new ClipData ();
    clipdata.samples = clip.samples;
    clipdata.channels = clip.channels;
    float[] dataFloat = new float[clip.samples*clip.channels];
    clip.GetData (dataFloat, 0);
    clipdata.samplesData = dataFloat;
    using (var fileStream = CreateEmpty(filepath)) {
        MemoryStream memstrm = new MemoryStream ();
        ConvertAndWrite(memstrm, clipdata);
        memstrm.WriteTo (fileStream);
        WriteHeader(fileStream, clip);
    }

    return true; // TODO: return false if there's a failure saving the file
}

public static AudioClip TrimSilence(AudioClip clip, float min) {
    var samples = new float[clip.samples];

    clip.GetData(samples, 0);

    return TrimSilence(new List<float>(samples), min, clip.channels, clip.frequency);
}

public static AudioClip TrimSilence(List<float> samples, float min, int channels, int hz) {
    return TrimSilence(samples, min, channels, hz, false, false);
}

public static AudioClip TrimSilence(List<float> samples, float min, int channels, int hz, bool _3D, bool stream) {
    int i;

    for (i=0; i<samples.Count; i++) {
        if (Mathf.Abs(samples[i]) > min) {
            break;
        }
    }

    samples.RemoveRange(0, i);

    for (i=samples.Count - 1; i>0; i--) {
        if (Mathf.Abs(samples[i]) > min) {
            break;
        }
    }

    samples.RemoveRange(i, samples.Count - i);

    var clip = AudioClip.Create("TempClip", samples.Count, channels, hz, _3D, stream);

    clip.SetData(samples.ToArray(), 0);

    return clip;
}

static FileStream CreateEmpty(string filepath) {
    var fileStream = new FileStream(filepath, FileMode.Create);
    byte emptyByte = new byte();

    for(int i = 0; i < HEADER_SIZE; i++) //preparing the header
    {
        fileStream.WriteByte(emptyByte);
    }

    return fileStream;
}

    static void ConvertAndWrite(MemoryStream memStream, ClipData clipData)
    {
        float[] samples = new float[clipData.samples*clipData.channels];

        samples = clipData.samplesData;

        Int16[] intData = new Int16[samples.Length];

        Byte[] bytesData = new Byte[samples.Length * 2];

        const float rescaleFactor = 32767; //to convert float to Int16

        for (int i = 0; i < samples.Length; i++)
        {
            intData[i] = (short)(samples[i] * rescaleFactor);
            //Debug.Log (samples [i]);
        }
        Buffer.BlockCopy(intData, 0, bytesData, 0, bytesData.Length);
        memStream.Write(bytesData, 0, bytesData.Length);
    }

    static void WriteHeader(FileStream fileStream, AudioClip clip) {

        var hz = clip.frequency;
        var channels = clip.channels;
        var samples = clip.samples;

        fileStream.Seek(0, SeekOrigin.Begin);

        Byte[] riff = System.Text.Encoding.UTF8.GetBytes("RIFF");
        fileStream.Write(riff, 0, 4);

        Byte[] chunkSize = BitConverter.GetBytes(fileStream.Length - 8);
        fileStream.Write(chunkSize, 0, 4);

        Byte[] wave = System.Text.Encoding.UTF8.GetBytes("WAVE");
        fileStream.Write(wave, 0, 4);

        Byte[] fmt = System.Text.Encoding.UTF8.GetBytes("fmt ");
        fileStream.Write(fmt, 0, 4);

        Byte[] subChunk1 = BitConverter.GetBytes(16);
        fileStream.Write(subChunk1, 0, 4);

        UInt16 two = 2;
        UInt16 one = 1;

        Byte[] audioFormat = BitConverter.GetBytes(one);
        fileStream.Write(audioFormat, 0, 2);

        Byte[] numChannels = BitConverter.GetBytes(channels);
        fileStream.Write(numChannels, 0, 2);

        Byte[] sampleRate = BitConverter.GetBytes(hz);
        fileStream.Write(sampleRate, 0, 4);

        Byte[] byteRate = BitConverter.GetBytes(hz * channels * 2); // sampleRate * bytesPerSample*number of channels, here 44100*2*2
        fileStream.Write(byteRate, 0, 4);

        UInt16 blockAlign = (ushort) (channels * 2);
        fileStream.Write(BitConverter.GetBytes(blockAlign), 0, 2);

        UInt16 bps = 16;
        Byte[] bitsPerSample = BitConverter.GetBytes(bps);
        fileStream.Write(bitsPerSample, 0, 2);

        Byte[] datastring = System.Text.Encoding.UTF8.GetBytes("data");
        fileStream.Write(datastring, 0, 4);

        Byte[] subChunk2 = BitConverter.GetBytes(samples * channels * 2);
        fileStream.Write(subChunk2, 0, 4);

        //      fileStream.Close();
    }
}

Really appreciate anyones time if they see why it won't save to mobile (specifically android atm).

Cœur
  • 37,241
  • 25
  • 195
  • 267
Diego
  • 371
  • 1
  • 3
  • 13
  • Why did you modify the original `SavWav` class? What happens when you try to save it without modification? If you modify something that's working for thousands of people and it stops working, it's worth mentioning or showing where you made the changes. – Programmer Dec 04 '17 at 21:16
  • I changed the file path as it wan't working the original was : var filepath = Path.Combine(Application.dataPath, filename); Nothing else touched the save function – Diego Dec 04 '17 at 21:17
  • Last time I used it, it worked. It works for many. When you say, it doesn't work, what do you mean? It doesn't save? You get errors? What makes you thin it's not saving? – Programmer Dec 04 '17 at 21:30
  • Hi again, it saved the file in my assets folder inside unity but when I built it to my android phone, I get a second freeze like it's trying to save then the message box on screen stays on "thinking" form the update function and the desired effect / response from the Json code which is sent back doesn't show in the message box – Diego Dec 04 '17 at 21:32
  • 1
    The freezing is likely the GetJSONText function because you are using a network(`HttpWebRequest`) API in the main Thread. You have to either use Unity's newtork API such as `WWW` or [`UnityWebRequest`](https://stackoverflow.com/questions/46003824/sending-http-requests-in-c-sharp-with-unity/46008025#46008025) in a coroutine function or use `HttpWebRequest` in another Thread. Note that coroutine is not a Thread. Unity's network system uses Thread too but hides that from you. It let's you use coroutine to manage it. Something `HttpWebRequest` do not support. – Programmer Dec 04 '17 at 21:37
  • 1
    Thanks for the direction and time, I'll amend it now. – Diego Dec 04 '17 at 21:38
  • I've posted a quick follow up question if you have the time to look it would be very much appreciated : https://stackoverflow.com/questions/47655165/400-bad-request-unity-wwwform-binary-data-with-audio-file – Diego Dec 05 '17 at 13:48

0 Answers0