I’m working on a project that receives audio data through TCP communication and then plays it once received.
On the receiving side. I opened another clientReceiveThread for the TCP socket communication, And the receiving code is
public void ListenForData()
{
float[] float_msg_temp;
float[] float_ms_z;
float[] float_try = { 3.0f, 3.5F, 4.0F, 4.5F, 5.0F };
socketConnection = new TcpClient("xxx.xxx.xx.xx", 8010);
NetworkStream myNetworkStream = socketConnection.GetStream();
while (true)
{
if (myNetworkStream.CanRead)
{
do
{
byte[] myReadBuffer = new byte[1024];
float data_len = myNetworkStream.Read(myReadBuffer, 0, myReadBuffer.Length);
StringBuilder myCompleteMessage = new StringBuilder();
int numberOfBytesRead = 0;
// use to change this to float array used by audio clip
float_msg_temp = ConvertByteArrayToFloat(myReadBuffer);
if (float_msg != null)
{
float_ms_z = new float[float_msg.Length + float_msg_temp.Length];
float_msg.CopyTo(float_ms_z, 0);
float_msg_temp.CopyTo(float_ms_z, float_msg.Length);
float_msg = float_ms_z;
}
else
{
float_ms_z = float_msg_temp;
float_msg = float_ms_z;
}
if (!myNetworkStream.DataAvailable)
System.Threading.Thread.Sleep(1);
} while (myNetworkStream.DataAvailable);
socketConnection.Close();
CanPlay = true;
// original idea: play the clip
Playonce(float_msg);
socketConnection = new TcpClient("xxx.xxx.xx.xx", 8010);
myNetworkStream = socketConnection.GetStream();
Debug.Log("reconnnect ");
}
else
{
Debug.Log("Sorry. You cannot read from this NetworkStream.");
}
}
}
static float[] ConvertByteArrayToFloat(byte[] bytes)
{
if (bytes.Length % 4 != 0) throw new ArgumentException();
float[] floats = new float[bytes.Length / 4];
for (int i = 0; i < floats.Length; i++)
{
floats[i] = BitConverter.ToSingle(bytes, i * 4);
}
return floats;
}
My original idea was to call a function to play the audio clip
void Playonce(float[] float_msg)
{
Debug.Log("Play Once");
if(CanPlay){
clip = AudioClip.Create("ClipName", float_msg.Length, channels, sampleRate, false);
clip.SetData(float_msg, 0);
audioSource.clip = clip;
audioSource.Play();
float_msg = null;
whole_msg = "";
CanPlay = false;
}
}
However, I got this error during testing:
UnityException: Construct_Internal can only be called from the main thread.
Constructors and field initializers will be executed from the loading thread when loading a scene.
Don’t use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.
So I tried another way using the Update function
void Update()
{
if (float_msg != null && CanPlay)
{
clip = AudioClip.Create("ClipName", float_msg.Length, channels, sampleRate, false);
clip.SetData(float_msg, 0);
audioSource.clip = clip;
audioSource.Play();
float_msg = null;
whole_msg = "";
CanPlay = false;
}
}
However, this method just doesn’t feel right to me. And sometimes I have problem of receiving only a short sentence of audio.
I would like to ask
- Is my current TCP connection method a good way of receiving data?
- Is there any better way of updating and playing the audio clip?