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).