Stream Microphone in Unity over RTP - c#

I'm working in a project where I need to stream microphone in Unity (2018.4.19f1) over the network using RTP. I've already found some examples of using Microphone class class but all of them are meant to record the audio to a file or to reproduce it using AudioClip. Right now I'm creating the microphone using the following code:
mic = Microphone.Start(null, true, 1, 44100); // Mono
Then After I have the following logic in the Update() loop:
private void Update()
{
if ((pos = Microphone.GetPosition(null)) > 0)
{
if (lastPos > pos) lastPos = 0;
if (pos - lastPos > 0)
{
int len = (pos - lastPos) * mic.channels;
float[] samples = new float[len];
mic.GetData(samples, lastPos);
//TODO: process samples
lastPos = pos;
}
}
}
I would like to know what's the audio format stored in samples and if it's possible to encode this audio and send it over RTP for example in unity. I would like to avoid using 3rd party assets whenever possible.

After some investigations finally I managed to implement a working script that basically captures the RAW PCM audio from the Microphone and stream it over RTP/UDP. RTP headers are populated by some hard-coded values, so probably you should adapt the code to your needs. Remember to add an "AudioSource" component to your gameobject.
To test the reception you can just go and use ffmpeg player (ffplay), for example:
ffplay rtp://0.0.0.0:your_port
Following is the C# script:
using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using UnityEngine;
public static class RtpPacket
{
public static void WriteHeader(byte[] rtpPacket
, int rtpVersion
, int rtpPadding
, int rtpExtension
, int rtpSrcCount
, int rtpMarker
, int rtpPayload)
{
rtpPacket[0] = (byte)((rtpVersion << 6) | (rtpPadding << 5) | (rtpExtension << 4) | rtpSrcCount);
rtpPacket[1] = (byte)((rtpMarker << 7) | (rtpPayload & 0x7F));
}
public static void WriteSequenceNumber(byte[] rtpPacket, uint emptySeqId)
{
rtpPacket[2] = ((byte)((emptySeqId >> 8) & 0xFF));
rtpPacket[3] = ((byte)((emptySeqId >> 0) & 0xFF));
}
public static void WriteTS(byte[] rtpPacket, uint ts)
{
rtpPacket[4] = ((byte)((ts >> 24) & 0xFF));
rtpPacket[5] = ((byte)((ts >> 16) & 0xFF));
rtpPacket[6] = ((byte)((ts >> 8) & 0xFF));
rtpPacket[7] = ((byte)((ts >> 0) & 0xFF));
}
public static void WriteSSRC(byte[] rtpPacket, uint ssrc)
{
rtpPacket[8] = ((byte)((ssrc >> 24) & 0xFF));
rtpPacket[9] = ((byte)((ssrc >> 16) & 0xFF));
rtpPacket[10] = ((byte)((ssrc >> 8) & 0xFF));
rtpPacket[11] = ((byte)((ssrc >> 0) & 0xFF));
}
}
public class AudioStreamer : MonoBehaviour
{
// Audio control variables
AudioClip mic;
int lastPos, pos;
// UDP Socket variables
private Socket socket;
private IPEndPoint RemoteEndPoint;
private UInt32 sequenecId = 0;
void SetRtpHeader(byte[] rtpPacket)
{
// Populate RTP Packet Header
// 0 - Version, P, X, CC, M, PT and Sequence Number
// 32 - Timestamp. H264 uses a 90kHz clock
// 64 - SSRC
// 96 - CSRCs (optional)
// nn - Extension ID and Length
// nn - Extension header
RtpPacket.WriteHeader(rtpPacket
, 2 // version
, 0 // padding
, 0 // extension
, 0 // csrc_count
, 1 // marker, set to one for last packet
, 11); // payload_type PCM 16bits BE signed
RtpPacket.WriteSequenceNumber(rtpPacket, sequenecId);
RtpPacket.WriteTS(rtpPacket, Convert.ToUInt32(DateTime.Now.Millisecond * 90));
RtpPacket.WriteSSRC(rtpPacket, 0);
sequenecId++;
}
void SendToServer(float[] samples)
{
const int RTP_HEADER_LEN = 12;
if (socket == null) return;
if (samples == null || samples.Length == 0) return;
// Convert audio from float to signed 16 bit PCM BigEndian and copy it to the byte array
var byteArray = new byte[samples.Length * sizeof(Int16)]; // to convert each sample float to Int16
int i = 0;
int j = 0;
while (i < samples.Length)
{
Int16 sample = Convert.ToInt16((samples[i] * Int16.MaxValue) / 100);
byteArray[j] = (byte)(sample & 0xFF);
byteArray[j + 1] = (byte)((sample >> 8) & 0xFF);
i = i + 1;
j = j + 2;
}
var dataToSend = byteArray.Length;
int maxEthMTU = 1400;
int offset = 0;
while (dataToSend > 0)
{
var bodyLen = Math.Min(dataToSend, maxEthMTU);
var rtpAudioData = new byte[RTP_HEADER_LEN + bodyLen];
SetRtpHeader(rtpAudioData);
System.Array.Copy(byteArray, offset, rtpAudioData, RTP_HEADER_LEN, bodyLen);
int dataSent = socket.SendTo(rtpAudioData, 0, rtpAudioData.Length, SocketFlags.None, RemoteEndPoint);
dataToSend = dataToSend - dataSent;
offset = offset + dataSent;
}
}
void Start()
{
RemoteEndPoint = new IPEndPoint(IPAddress.Parse("your_server_ip"), your_server_port);
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
mic = Microphone.Start(null, true, 1, 44100); // Mono
}
private void Update()
{
if ((pos = Microphone.GetPosition(null)) > 0)
{
if (lastPos > pos) lastPos = 0;
if (pos - lastPos > 0)
{
// Allocate the space for the new sample.
int len = (pos - lastPos) * mic.channels;
float[] samples = new float[len];
mic.GetData(samples, lastPos);
SendToServer(samples);
lastPos = pos;
}
}
}
void OnDestroy()
{
Microphone.End(null);
}
}

Related

How to send message to WebSocket in VB.NET?

How can i send a message from my WinForm VB.NET project to my WebSocket?
Actually i was able to do it by sending proper byte data to the NetworkStream in c# via the following code in c# which codify the string message to byte array:
private static byte[] EncodeMessageToSend(string message)
{
byte[] response;
byte[] bytesRaw = Encoding.UTF8.GetBytes(message);
byte[] frame = new byte[10];
int indexStartRawData = -1;
int length = bytesRaw.Length;
frame[0] = (Byte)129;
if (length <= 125)
{
frame[1] = (Byte)length;
indexStartRawData = 2;
}
else if (length >= 126 && length <= 65535)
{
frame[1] = (Byte)126;
frame[2] = (Byte)((length >> 8) & 255);
frame[3] = (Byte)(length & 255);
indexStartRawData = 4;
}
else
{
frame[1] = (Byte)127;
frame[2] = (Byte)((length >> 56) & 255);
frame[3] = (Byte)((length >> 48) & 255);
frame[4] = (Byte)((length >> 40) & 255);
frame[5] = (Byte)((length >> 32) & 255);
frame[6] = (Byte)((length >> 24) & 255);
frame[7] = (Byte)((length >> 16) & 255);
frame[8] = (Byte)((length >> 8) & 255);
frame[9] = (Byte)(length & 255);
indexStartRawData = 10;
}
response = new Byte[indexStartRawData + length];
Int32 i, reponseIdx = 0;
//Add the frame bytes to the reponse
for (i = 0; i < indexStartRawData; i++)
{
response[reponseIdx] = frame[i];
reponseIdx++;
}
//Add the data bytes to the response
for (i = 0; i < length; i++)
{
response[reponseIdx] = bytesRaw[i];
reponseIdx++;
}
return response;
}
Then i jsut used that code with stream.Write but what will be an equivalent code in VB? or is there another way to send a message to the WebSocket?
PS: the websocket doesn't accept a simple string or a simple string converted to byte[] i've yet tryed it.
I send a message over a websocket by using System.Net.Websockets.Websocket which has an asynchronous send function:
Dim bytes = Encoding.UTF8.GetBytes(message)
Await socket.SendAsync(New ArraySegment(Of Byte)(bytes, 0, bytes.length), Websockets.WebsocketMessageType.Text, True, cancelToken)
Note that chunking may be required depending on the maximum frame size; if so, send each chunk but the last with the penultimate parameter as False and adjust the initial position and length of the array segment accordingly.
(Addendum: I've just noticed that the initial position and segment length are not required in the ArraySegment(Of Byte) construction if making it out of the whole array, but I'm going to leave it in place here because it will be clearer what changes would need to be made to chunk the message.)

How can I convert a byte[] to an int in c# without using any System helper functions?

To convert a byte[] to an int, I would normally use BitConverter.ToInt32, but I'm currently working within a framework called UdonSharp that restricts access to most System methods, so I'm unable to use that helper function. I was able to do the reverse operation manually (converting int to byte[]) quite easily like so:
private byte[] GetBytes(int target)
{
byte[] bytes = new byte[4];
bytes[0] = (byte)(target >> 24);
bytes[1] = (byte)(target >> 16);
bytes[2] = (byte)(target >> 8);
bytes[3] = (byte)target;
return bytes;
}
But I'm struggling to get it working the other way around. Would greatly appreciate any help!
You can check the code here: https://referencesource.microsoft.com/#mscorlib/system/bitconverter.cs
#if BIGENDIAN
public static readonly bool IsLittleEndian /* = false */;
#else
public static readonly bool IsLittleEndian = true;
#endif
[System.Security.SecuritySafeCritical] // auto-generated
public static unsafe int ToInt32(byte[] value, int startIndex) {
if (value == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
}
if ((uint) startIndex >= value.Length) {
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);
}
if (startIndex > value.Length - 4) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
}
Contract.EndContractBlock();
fixed(byte * pbyte = & value[startIndex]) {
if (startIndex % 4 == 0) { // data is aligned
return *((int * ) pbyte);
} else {
if (IsLittleEndian) {
return ( * pbyte) | ( * (pbyte + 1) << 8) | ( * (pbyte + 2) << 16) | ( * (pbyte + 3) << 24);
} else {
return ( * pbyte << 24) | ( * (pbyte + 1) << 16) | ( * (pbyte + 2) << 8) | ( * (pbyte + 3));
}
}
}
}

Convert esp8266 code(part of it) to c/c++ or c#

Hi im trying to repeat this wifi baby monitor project based on esp8266:
baby monitor project
But instead of receiving data on another esp8266, I want to receive data on pc.
I'm a c # programmer and I've encountered an problem of understanding c/c++ pointers how arrays works here and receive udp.
this is esp8266 receiver source code which works without any problems, but data that it receives, passes them to DAC. And i cant figure out where i can read just values one by one which was readed by esp8266 transmiter from ADC. Also readed data from ADC is 12 bit and author of original code use all 16 bit with some compression to transfer more data, and this compression part is what i have difficulty to uderstand it
#include <Wire.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <WiFiUdp.h>
#include "ESP8266mDNS.h"
#include <ArduinoOTA.h>
//#include "wifi_params.h"
const int mySDA = D7;
const int mySCL = D6;
const int AMPLI_MUTE_PIN = D2;
const int AMPLI_SHUTDOWN_PIN = D1;
const int RIGHT_BTN = D3;
const int LEFT_BTN = D4;
const int LED1 = D8;
const int udp_recv_port = 45990;
WiFiUDP udp;
TwoWire i2c;
#define NB_DATA_BUFS 5
uint16_t data_buf[NB_DATA_BUFS][700]; // data buffer, N buffered
unsigned int current_play_data_buf; // current data buf being played
unsigned int play_data_buf_pos; // position in the ADC data buffer
unsigned int current_recv_data_buf; // current data buf being received
bool play_waiting = true;
bool amplifier_stopped = false;
long play_waiting_at;
bool left_btn_pressed;
bool right_btn_pressed;
#define ICACHE_RAM_ATTR __attribute__((section(".iram.text")))
#define twi_sda mySDA
#define twi_scl mySCL
#define twi_dcount 0
#define twi_clockStretchLimit 10
#define SDA_LOW() (GPES = (1 << twi_sda)) //Enable SDA (becomes output and since GPO is 0 for the pin, it will pull the line low)
#define SDA_HIGH() (GPEC = (1 << twi_sda)) //Disable SDA (becomes input and since it has pullup it will go high)
#define SDA_READ() ((GPI & (1 << twi_sda)) != 0)
#define SCL_LOW() (GPES = (1 << twi_scl))
#define SCL_HIGH() (GPEC = (1 << twi_scl))
#define SCL_READ() ((GPI & (1 << twi_scl)) != 0)
static void twi_delay(unsigned char v) {
unsigned int i;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
unsigned int reg;
for (i = 0; i<v; i++) reg = GPI;
#pragma GCC diagnostic pop
}
static inline ICACHE_RAM_ATTR bool twi_write_start(void) {
SCL_HIGH();
SDA_HIGH();
if (SDA_READ() == 0) return false;
SDA_LOW();
return true;
}
static inline ICACHE_RAM_ATTR bool twi_write_stop(void) {
uint32_t i = 0;
SCL_LOW();
SDA_LOW();
SCL_HIGH();
while (SCL_READ() == 0 && (i++) < twi_clockStretchLimit); // Clock stretching
SDA_HIGH();
return true;
}
static inline ICACHE_RAM_ATTR bool twi_write_bit(bool bit) {
uint32_t i = 0;
SCL_LOW();
if (bit) SDA_HIGH();
else SDA_LOW();
twi_delay(twi_dcount + 1);
SCL_HIGH();
while (SCL_READ() == 0 && (i++) < twi_clockStretchLimit);// Clock stretching
return true;
}
static inline ICACHE_RAM_ATTR bool twi_read_bit(void) {
uint32_t i = 0;
SCL_LOW();
SDA_HIGH();
twi_delay(twi_dcount + 2);
SCL_HIGH();
while (SCL_READ() == 0 && (i++) < twi_clockStretchLimit);// Clock stretching
bool bit = SDA_READ();
return bit;
}
static inline ICACHE_RAM_ATTR bool twi_write_byte(unsigned char byte) {
unsigned char bit;
for (bit = 0; bit < 8; bit++) {
twi_write_bit(byte & 0x80);
byte <<= 1;
}
return !twi_read_bit();//NACK/ACK
}
static inline ICACHE_RAM_ATTR unsigned char twi_read_byte(bool nack) {
unsigned char byte = 0;
unsigned char bit;
for (bit = 0; bit < 8; bit++) byte = (byte << 1) | twi_read_bit();
twi_write_bit(nack);
return byte;
}
unsigned char inline ICACHE_RAM_ATTR mytwi_writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop) {
unsigned int i;
if (!twi_write_start()) return 4;//line busy
if (!twi_write_byte(((address << 1) | 0) & 0xFF)) {
if (sendStop) twi_write_stop();
return 2; //received NACK on transmit of address
}
for (i = 0; i<len; i++) {
if (!twi_write_byte(buf[i])) {
if (sendStop) twi_write_stop();
return 3;//received NACK on transmit of data
}
}
if (sendStop) twi_write_stop();
i = 0;
while (SDA_READ() == 0 && (i++) < 10) {
SCL_LOW();
SCL_HIGH();
}
return 0;
}
static inline ICACHE_RAM_ATTR uint8_t DAC(uint16_t value)
{
/* value is 76543210 XXXXBA98
per the datasheet for fast write:
1 1 0 0 A2 A1 A0 0 <ACK> 0 0 PD1 PD0 D11 D10 D9 D8 <ACK> D7 D6 D5 D4 D3 D2 D1 D0 <ACK>
*/
uint8_t buf[2] = { (value >> 8) & 0x0F, (value & 0xFF) };
int ret = mytwi_writeTo(0x60, buf, 2, true);
Serial.println(value);
return ret;
}
void ICACHE_RAM_ATTR playsample_isr(void)
{
if (play_waiting) {
return;
}
DAC(data_buf[current_play_data_buf][play_data_buf_pos]);
play_data_buf_pos++;
if (play_data_buf_pos >= sizeof(data_buf[0]) / sizeof(data_buf[0][0])) {
play_data_buf_pos = 0;
current_play_data_buf++;
if (current_play_data_buf == NB_DATA_BUFS) {
current_play_data_buf = 0;
}
if (current_play_data_buf == current_recv_data_buf) {
play_waiting = true;
play_waiting_at = micros();
}
}
}
void ota_onstart(void)
{
// Disable timer when an OTA happens
timer1_detachInterrupt();
timer1_disable();
}
void ota_onprogress(unsigned int sz, unsigned int total)
{
Serial.print("OTA: "); Serial.print(sz); Serial.print("/"); Serial.print(total);
Serial.print("="); Serial.print(100 * sz / total); Serial.println("%");
}
void ota_onerror(ota_error_t err)
{
Serial.print("OTA ERROR:"); Serial.println((int)err);
}
void left_btn_intr()
{
left_btn_pressed = 1;
}
void right_btn_intr()
{
right_btn_pressed = 1;
}
void setup(void)
{
Serial.begin(115200);
Serial.println("I was built on " __DATE__ " at " __TIME__ "");
i2c.begin(mySDA, mySCL);
i2c.setClock(400000);
WiFi.mode(WIFI_STA);
WiFi.begin("valik 2", "299745buhlo");
WiFi.setSleepMode(WIFI_MODEM_SLEEP);
Serial.print("Connecting to wifi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Cnnectd to ");
Serial.println("valik 2");
Serial.print("IP ");
Serial.println(WiFi.localIP());
ArduinoOTA.onStart(ota_onstart);
ArduinoOTA.onError(ota_onerror);
ArduinoOTA.onProgress(ota_onprogress);
ArduinoOTA.setHostname("bb-recv");
ArduinoOTA.begin();
timer1_isr_init();
timer1_attachInterrupt(playsample_isr);
timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
timer1_write(clockCyclesPerMicrosecond() / 16 * 50); //50us = 20 kHz sampling freq
udp.begin(udp_recv_port);
pinMode(AMPLI_MUTE_PIN, OUTPUT);
pinMode(AMPLI_SHUTDOWN_PIN, OUTPUT);
digitalWrite(AMPLI_SHUTDOWN_PIN, 0);
digitalWrite(AMPLI_MUTE_PIN, 0);
pinMode(LEFT_BTN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(LEFT_BTN), left_btn_intr, FALLING);
pinMode(RIGHT_BTN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(RIGHT_BTN), right_btn_intr, FALLING);
pinMode(LED1, OUTPUT);
digitalWrite(LED1, 0);
}
int do_undelta7(const uint8_t *val, int sz, uint16_t *out)
{
// Implement delta 7 decompression.
// First bit = 0 <=> uncompressed 15 bits following
// First bit = 1 <=> 7 bits follow representing delta
// must switch to big endian...
uint16_t last = 0;
uint8_t *ptr = (uint8_t *)&out[0];
const uint8_t *start = ptr;
for (int i = 0; i < sz; i++) {
uint16_t *ptr16 = (uint16_t *)ptr;
const int8_t firstbyte = val[i];
if (firstbyte & 0x80) {
// Delta7 compressed
// byte is CSMMMMMM
int8_t delta = firstbyte & 0x3F;
if (firstbyte & 0x40) {
delta = -delta;
}
const uint16_t value = last + delta;
*ptr16 = value;
ptr += 2;
last = value;
}
else {
// uncompressed -- switch bytes back to LE
*ptr++ = val[i + 1];
*ptr++ = val[i];
last = val[i + 1] | val[i] << 8;
i++;
}
}
return ptr - start;
}
void loop(void)
{
ArduinoOTA.handle();
int sz = udp.parsePacket();
//Serial.println(current_play_data_buf);
if (sz) {
uint8_t buf[sz];
udp.read(&buf[0], sz);
current_recv_data_buf++;
if (current_recv_data_buf == NB_DATA_BUFS) {
current_recv_data_buf = 0;
if (current_recv_data_buf == current_play_data_buf && !play_waiting) {
Serial.println("buffer overflow when receiving");
}
}
do_undelta7(buf, sz, &data_buf[current_recv_data_buf][0]);
if (play_waiting) {
Serial.print("Restarting play, was waiting (us)"); Serial.println(micros() - play_waiting_at);
// Re-enable *then* unmute in that order to avoid pops
digitalWrite(AMPLI_SHUTDOWN_PIN, 1);
digitalWrite(AMPLI_MUTE_PIN, 1);
play_waiting = false;
amplifier_stopped = false;
digitalWrite(LED1, 1);
}
Serial.println("");
}
// If not playing anything, but amplifier is still up
if (!amplifier_stopped && play_waiting) {
if ((micros() - play_waiting_at) > 2000 * 1000) {
// If nothing has been played for two seconds, shut down the amplifier
Serial.println("Shutting down amplifier!");
digitalWrite(AMPLI_SHUTDOWN_PIN, 0);
digitalWrite(AMPLI_MUTE_PIN, 0);
amplifier_stopped = true;
digitalWrite(LED1, 0);
}
}
if (left_btn_pressed) {
left_btn_pressed = 0;
digitalWrite(AMPLI_MUTE_PIN, 0);
digitalWrite(AMPLI_SHUTDOWN_PIN, 0);
}
if (right_btn_pressed) {
digitalWrite(AMPLI_SHUTDOWN_PIN, 1);
digitalWrite(AMPLI_MUTE_PIN, 1);
udp.beginPacket(udp.remoteIP(), 45990);
udp.write("sendnow");
udp.endPacket();
right_btn_pressed = 0;
}
// If the amplifier is stopped, add a delay for power saving
if (amplifier_stopped) {
delay(10);
}
}
This is my attempt to translate code to c++ for windows. But i encountered problem where programm just freeze without any errors and without closing.
#include "stdafx.h"
#include <winsock2.h>
#include <stdio.h>
#include <cstdint>
#include <ctime>
#pragma comment (lib, "ws2_32.lib")
#define NB_DATA_BUFS 5
uint16_t data_buf[NB_DATA_BUFS][700]; // data buffer, N buffered
unsigned int current_play_data_buf; // current data buf being played
unsigned int play_data_buf_pos; // position in the ADC data buffer
unsigned int current_recv_data_buf; // current data buf being received
bool play_waiting = true;
bool amplifier_stopped = false;
long play_waiting_at;
bool left_btn_pressed;
bool right_btn_pressed;
void InitWinsock()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
}
int do_undelta7(const uint8_t *val, int sz, uint16_t *out)
{
// Implement delta 7 decompression.
// First bit = 0 <=> uncompressed 15 bits following
// First bit = 1 <=> 7 bits follow representing delta
// must switch to big endian...
uint16_t last = 0;
uint8_t *ptr = (uint8_t *)&out[0];
const uint8_t *start = ptr;
for (int i = 0; i < sz; i++) {
uint16_t *ptr16 = (uint16_t *)ptr;
const int8_t firstbyte = val[i];
if (firstbyte & 0x80) {
// Delta7 compressed
// byte is CSMMMMMM
int8_t delta = firstbyte & 0x3F;
if (firstbyte & 0x40) {
delta = -delta;
}
const uint16_t value = last + delta;
*ptr16 = value;
ptr += 2;
last = value;
}
else {
// uncompressed -- switch bytes back to LE
*ptr++ = val[i + 1];
*ptr++ = val[i];
last = val[i + 1] | val[i] << 8;
i++;
}
}
return ptr - start;
}
void DAC(uint16_t value)
{
/* value is 76543210 XXXXBA98
per the datasheet for fast write:
1 1 0 0 A2 A1 A0 0 <ACK> 0 0 PD1 PD0 D11 D10 D9 D8 <ACK> D7 D6 D5 D4 D3 D2 D1 D0 <ACK>
*/
uint8_t buf[2] = { (value >> 8) & 0x0F, (value & 0xFF) };
printf("%u\n", value & 0xFFF);
}
int _tmain(int argc, _TCHAR* argv[])
{
SOCKET socketC;
InitWinsock();
struct sockaddr_in serverInfo;
int len = 2000;
serverInfo.sin_family = AF_INET;
serverInfo.sin_port = htons(45990);
serverInfo.sin_addr.s_addr = inet_addr("192.168.1.105");
socketC = socket(AF_INET, SOCK_DGRAM, 0);
char buffers[16];
ZeroMemory(buffers, sizeof(buffers));
sendto(socketC, buffers, sizeof(IReleaseMarshalBuffers), 0, (sockaddr*)&serverInfo, len);
while (1)
{
sockaddr_in from;
const int paketSize = sizeof(from);
int r = paketSize;
char buffer[paketSize];
sprintf(buffer, "%.7s", "sendnow");
if (strcmp(buffer, "exit") == 0)
break;
recvfrom(socketC, buffer, sizeof(buffer), 0, (sockaddr*)&serverInfo, &len);
uint8_t buf[sizeof(buffer)];
uint8_t * bufeerPntr = (uint8_t*)buffer;
uint8_t * bufPntr = (uint8_t*)buffer;
for(int i=0;i<sizeof(buffer);i++)
{
buf[i] = buffer[i];
}
//udp.read(&buf[0], sizeof(buffer));
current_recv_data_buf++;
if (current_recv_data_buf == NB_DATA_BUFS) {
current_recv_data_buf = 0;
if (current_recv_data_buf == current_play_data_buf && !play_waiting) {
printf("buffer overflow when receiving\n");
}
}
do_undelta7(buf, sizeof(buffer), &data_buf[current_recv_data_buf][0]);
}
closesocket(socketC);
return 0;
}
This is my attempt to translate decoding part to c# (c# is much easier for me to understand) but i forced to use pointers and strange * and & things which i have difficulty to understand:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class UDPListener
{
private const int listenPort = 45990;
public static int Main()
{
bool done = false;
UdpClient listener = new UdpClient(listenPort);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Parse("192.168.1.3"), listenPort);
string received_data;
int BUFSIZE = 700;
byte[] receive_byte_array;
uint current_recv_data_buf = 1;
while (!done)
{
Console.WriteLine("Waiting for broadcast");
receive_byte_array = listener.Receive(ref groupEP);
Console.WriteLine("Received a broadcast from {0}", groupEP.ToString());
received_data = Encoding.ASCII.GetString(receive_byte_array, 0, receive_byte_array.Length);
unsafe
{
UInt16*[,] data_buf = new UInt16*[5, 700];
int sz = receive_byte_array.Length;
if (sz > 0)
{
byte[] buf = new byte[receive_byte_array.Length];
UInt16* f = stackalloc UInt16[2000];
//udp.read(&buf[0], sz);
buf = receive_byte_array;
current_recv_data_buf++;
UInt16 last = 0;
UInt16* #out1 = stackalloc UInt16[800];
for (int i = 0; i < 800; i++)
{
#out1[i] = (char)i;
}
//UIntPtr* ptr = (UIntPtr*)&#out[0];
UIntPtr* ptr = (UIntPtr*)&#out1[0];
UIntPtr* start = ptr;
for (int i = 0; i < sz; i++)
{
UIntPtr* ptr16 = ptr;
byte firstbyte = buf[i];
if ((firstbyte & 0x80) != 0)
{
// Delta7 compressed
// byte is CSMMMMMM
byte delta = (byte)(firstbyte & 0x3F);
if ((firstbyte & 0x40) != 0)
{
delta = (byte)(0 - delta);
}
UInt16 value = (UInt16)(last + delta);
*ptr16 = (UIntPtr)value;
ptr += 2;
last = value;
}
else
{
*ptr++ = (UIntPtr)buf[i + 1];
*ptr++ = (UIntPtr)buf[i];
last = (UInt16)(buf[i + 1] | buf[i] << 8);
i++;
}
}
for (int i = 0; i < 91; i++)
{
System.Console.WriteLine(#out1[i]);
}
string b = "";
}
}
}
listener.Close();
return 0;
}
} // end of class UDPListener
udp.read(&buf[0], sz); copies the received UDP packet into buffer buf. The function do_undelta7 then makes a decompression of the data in the input buffer to output buffer data_buf[current_recv_data_buf]. data_buf is array of buffers. The interrupt playsample_isr plays the content of the buffers in data_buf.
i missed an unsigned byte in do_undelta7
so now it's decoding well
c#
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Media;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using NAudio.Wave;
public class UDPListener
{
uint current_recv_data_buf;
static int NB_DATA_BUFS = 5;
static UInt16[] data_buf = new UInt16[700];
uint current_play_data_buf; // current data buf being played
uint play_data_buf_pos; // position in the ADC data buffer
private const int listenPort = 45990;
public static unsafe int Main()
{
bool done = false;
UdpClient listener = new UdpClient(listenPort);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Parse("192.168.1.3"), listenPort);
int BUFSIZE = 700;
byte[] receive_byte_array;
uint current_recv_data_buf = 1;
List<byte> tenBuffsToPlay = new List<byte>();
int iterBuffsToPLay = 0;
byte[] byteArrToPlay = new byte[data_buf.Length * 2];
byte[] byte10ArrToPlay;
int pktcount = 0;
var sampleRate = 20000;
var frequency = 500;
var amplitude = 0.2;
var seconds = 5;
while (!done)
{
receive_byte_array = listener.Receive(ref groupEP);
if (receive_byte_array.Length > 0)
{
Console.WriteLine("received !"+pktcount++);
int sz = receive_byte_array.Length;
unsafe
{
byte[] buf = new byte[sz];
buf = receive_byte_array;
fixed (UInt16* data_bufPtr = &data_buf[0])
fixed (byte* ptrbuf = buf)
do_undelta7(ptrbuf, sz, data_bufPtr);
//string firstPart = "";
//string secondPart = "";
for (int i =0;i<data_buf.Length;i++)
{
//Console.WriteLine("Hex: {0:X}", data_buf[i]);
byteArrToPlay[i*2] = (byte)((data_buf[i] >> 8)&0x0f);
byteArrToPlay[(i*2)+1] = (byte)(data_buf[i] & 0xff);
//firstPart = Convert.ToString(byteArrToPlay[i], 2).PadLeft(4, '0');
//Console.Write(firstPart);
//secondPart = Convert.ToString(byteArrToPlay[i+1], 2).PadLeft(4, '0');
//Console.Write(secondPart+"\n");
}
//byteArrToPlay = data_buf.SelectMany(BitConverter.GetBytes).ToArray();
//foreach (var Arr in byteArrToPlay)
// {
// Console.WriteLine("Hex: {0:X}", Arr);
// }
tenBuffsToPlay.AddRange(byteArrToPlay);
iterBuffsToPLay++;
if (iterBuffsToPLay == 3)
{
byte10ArrToPlay = tenBuffsToPlay.ToArray();
/*var raw = new byte[sampleRate * seconds * 2];
var multiple = 2.0 * frequency / sampleRate;
for (int n = 0; n < sampleRate * seconds; n++)
{
var sampleSaw = ((n * multiple) % 2) - 1;
var sampleValue = sampleSaw > 0 ? amplitude : -amplitude;
var sample = (short)(sampleValue * Int16.MaxValue);
var bytes = BitConverter.GetBytes(sample);
raw[n * 2] = bytes[0];
raw[n * 2 + 1] = bytes[1];
}*/
var ms = new MemoryStream(byte10ArrToPlay);
var rs = new RawSourceWaveStream(ms, new WaveFormat(sampleRate, 16, 1));
var wo = new WaveOutEvent();
wo.Init(rs);
wo.Play();
/*while (wo.PlaybackState == PlaybackState.Playing)
{
Thread.Sleep(1);
}*/
//wo.Dispose();
/*using (MemoryStream ms = new MemoryStream())
{
WriteWavHeader(ms, false, 1, 16, 20000, (byte10ArrToPlay.Length / 2 - 45));
// Construct the sound player
ms.Write(byte10ArrToPlay, 0, byte10ArrToPlay.Length);
ms.Position = 0;
SoundPlayer player = new SoundPlayer(ms);
player.Play();
}*/
tenBuffsToPlay.Clear();
iterBuffsToPLay = 0;
}
}
}
}
listener.Close();
return 0;
}
static unsafe long do_undelta7(byte* val, int sz, UInt16* outArray)
{
// Implement delta 7 decompression.
// First bit = 0 <=> uncompressed 15 bits following
// First bit = 1 <=> 7 bits follow representing delta
// must switch to big endian...
UInt16 last = 0;
byte* ptr = (byte*)&outArray[0];
byte* start = ptr;
for (int i = 0; i < sz; i++)
{
UInt16* ptr16 = (UInt16*)ptr;
byte firstbyte = val[i];
var bit = (firstbyte & (1 << 8 - 1)) != 0;
if (bit == true)
{
// Delta7 compressed
// byte is CSMMMMMM
sbyte delta = (sbyte)(firstbyte & 0x3f);
bit = (firstbyte & (1 << 7 - 1)) != 0;
if (bit == true)
{
delta = (sbyte)(0x0 - delta);
}
UInt16 value = (UInt16)(last + delta);
*ptr16 = value;
ptr += 2;
last = value;
}
else
{
// uncompressed -- switch bytes back to LE
*ptr++ = val[i + 1];
*ptr++ = val[i];
last = (UInt16)(val[i + 1] | val[i] << 8);
i++;
}
}
return ptr - start;
}
private static void WriteWavHeader(MemoryStream stream, bool isFloatingPoint, ushort channelCount, ushort bitDepth, int sampleRate, int totalSampleCount)
{
stream.Position = 0;
stream.Write(Encoding.ASCII.GetBytes("RIFF"), 0, 4);
stream.Write(BitConverter.GetBytes((2* totalSampleCount) + 36), 0, 4);
stream.Write(Encoding.ASCII.GetBytes("WAVE"), 0, 4);
stream.Write(Encoding.ASCII.GetBytes("fmt "), 0, 4);
stream.Write(BitConverter.GetBytes(16), 0, 4);
stream.Write(BitConverter.GetBytes((ushort)(isFloatingPoint ? 3 : 1)), 0, 2);
stream.Write(BitConverter.GetBytes(channelCount), 0, 2);
stream.Write(BitConverter.GetBytes(sampleRate), 0, 4);
stream.Write(BitConverter.GetBytes(sampleRate * 2), 0, 4);
stream.Write(BitConverter.GetBytes((ushort)2), 0, 2);
stream.Write(BitConverter.GetBytes(16), 0, 2);
stream.Write(Encoding.ASCII.GetBytes("data"), 0, 4);
stream.Write(BitConverter.GetBytes(2 * totalSampleCount), 0, 4);
}
} // end of class UDPListener

Read response from device modbus C#

Good afternoon everyone. Faced a problem: it is necessary to read data from the devices using the modbus protocol. I can not understand how to get a response from the device.
So I call my function, but how do I get data from the device? Maybe someone can help.
P.S. Please, do not advise using ready-made libraries like nmodbus - with them I did everything, I want to try it myself. Thank you.
static void Main(string[] args)
{
var serial = new SerialPort("COM6", 19200);
serial.Handshake = Handshake.None;
serial.Parity = Parity.None;
serial.DataBits = 8;
serial.StopBits = StopBits.One;
serial.Open();
serial.Write(ReadHoldingRegister(2, 1024, 16), 0, 8);
serial.Close();
Console.ReadLine();
}
#region Function 3
public static byte[] ReadHoldingRegister(int id, int startAddress, int length)
{
byte[] data = new byte[8];
byte High, Low;
data[0] = Convert.ToByte(id);
data[1] = Convert.ToByte(3);
byte[] _adr = BitConverter.GetBytes(startAddress - 1);
data[2] = _adr[1];
data[3] = _adr[0];
byte[] _length = BitConverter.GetBytes(length);
data[4] = _length[1];
data[5] = _length[0];
myCRC(data, 6, out High, out Low);
data[6] = Low;
data[7] = High;
return data;
}
#endregion
#region CRC
public static void myCRC(byte[] message, int length, out byte CRCHigh, out byte CRCLow)
{
ushort CRCFull = 0xFFFF;
for (int i = 0; i < length; i++)
{
CRCFull = (ushort)(CRCFull ^ message[i]);
for (int j = 0; j < 8; j++)
{
if ((CRCFull & 0x0001) == 0)
CRCFull = (ushort)(CRCFull >> 1);
else
{
CRCFull = (ushort)((CRCFull >> 1) ^ 0xA001);
}
}
}
CRCHigh = (byte)((CRCFull >> 8) & 0xFF);
CRCLow = (byte)(CRCFull & 0xFF);
}
#endregion
}
Processing a response is not "simple", you really need to account for errors, inter character and inter packet timing, etc. It should also probably be on a separate high-priority thread. The Modbus specs have some processing state diagrams that illustrate what (at a minimum) should be done. However, as a VERY SIMPLE, brute force, polling EXAMPLE, you could start with something like this:
static Queue<byte> GetResponse(SerialPort serial, int timeoutMSecs)
{
Queue<byte> response = new Queue<byte>();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
while (stopwatch.ElapsedMilliseconds < timeoutMSecs)
{
if (serial.BytesToRead == 0) continue; // Nothing received, continue waiting
response.Enqueue((byte)serial.ReadByte()); // Add rx byte to queue
stopwatch.Reset(); // Reset timeout
if (response.Count > 255) break; // Maximum RTU packet?
}
return response;
}
In your main function
static void Main(string[] args)
{
...
serial.DiscardInBuffer(); // Clear any remaining rx bytes
serial.Write(ReadHoldingRegister(2, 1024, 16), 0, 8);
Queue<byte> response = GetResponse(serial, 5);
if (response.Count > 0)
{
// Process Response
// 1) check the CRC
// 2) if crc ok, check if it is an exception response
// 3) if not an exception check that it "matches" the request
// ...
}
...
}

Optimize bit reader for ReadInt on datastream

Could anyone help me optimize this piece of code? Its currently a large bottleneck as it gets called very often. Even a 25% speed improvement would be significant.
public int ReadInt(int length)
{
if (Position + length > Length)
throw new BitBufferException("Not enough bits remaining.");
int result = 0;
while (length > 0)
{
int off = Position & 7;
int count = 8 - off;
if (count > length)
count = length;
int mask = (1 << count) - 1;
int bits = (Data[Position >> 3] >> off);
result |= (bits & mask) << (length - count);
length -= count;
Position += count;
}
return result;
}
Best answer would go to fastest solution. Benchmarks done with dottrace. Currently this block of code takes up about 15% of the total cpu time. Lowest number wins best answer.
EDIT: Sample usage:
public class Auth : Packet
{
int Field0;
int ProtocolHash;
int Field1;
public override void Parse(buffer)
{
Field0 = buffer.ReadInt(9);
ProtocolHash = buffer.ReadInt(32);
Field1 = buffer.ReadInt(8);
}
}
Size of Data is variable but in most cases 512 bytes;
How about using pointers and unsafe context? You didn't say anything about your input data, method context, etc. so I tried to deduct all of these by myself.
public class BitTest
{
private int[] _data;
public BitTest(int[] data)
{
Length = data.Length * 4 * 8;
// +2, because we use byte* and long* later
// and don't want to read outside the array memory
_data = new int[data.Length + 2];
Array.Copy(data, _data, data.Length);
}
public int Position { get; private set; }
public int Length { get; private set; }
and ReadInt method. Hope comments give a little light on the solution:
public unsafe int ReadInt(int length)
{
if (Position + length > Length)
throw new ArgumentException("Not enough bits remaining.");
// method returns int, so getting more then 32 bits is pointless
if (length > 4 * 8)
throw new ArgumentException();
//
int bytePosition = Position / 8;
int bitPosition = Position % 8;
Position += length;
// get int* on array to start with
fixed (int* array = _data)
{
// change pointer to byte*
byte* bt = (byte*)array;
// skip already read bytes and change pointer type to long*
long* ptr = (long*)(bt + bytePosition);
// read value from current pointer position
long value = *ptr;
// take only necessary bits
value &= (1L << (length + bitPosition)) - 1;
value >>= bitPosition;
// cast value to int before returning
return (int)value;
}
}
}
I didn't test the method, but would bet it's much faster then your approach.
My simple test code:
var data = new[] { 1 | (1 << 8 + 1) | (1 << 16 + 2) | (1 << 24 + 3) };
var test = new BitTest(data);
var bytes = Enumerable.Range(0, 4)
.Select(x => test.ReadInt(8))
.ToArray();
bytes contains { 1, 2, 4, 8}, as expected.
I Don't know if this give you a significant improvements but it should give you some numbers.
Instead of creating new int variables inside the loop (this requires a time to create) let reserved those variables before entering the loop.
public int ReadInt(int length)
{
if (Position + length > Length)
throw new BitBufferException("Not enough bits remaining.");
int result = 0;
int off = 0;
int count = 0;
int mask = 0;
int bits = 0
while (length > 0)
{
off = Position & 7;
count = 8 - off;
if (count > length)
count = length;
mask = (1 << count) - 1;
bits = (Data[Position >> 3] >> off);
result |= (bits & mask) << (length - count);
length -= count;
Position += count;
}
return result;
}
HOPE THIS increase your performance even a bit

Categories

Resources