I am using the UTC timezone and the time in my application to get data. In the application, wherever the user can get the UTC time and used for getting the data.
I used this method to get the UTC time.
String format = "yyyy-MM-dd HH:mm:ss";
final SimpleDateFormat sdf = new SimpleDateFormat(format);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
String utcTime = sdf.format(new Date());
The application is working fine. But it is converting the system time to UTC time. The problem is that sometimes the user can change the time to the wrong time. So the data is not coming.
Case for the above issue is:
For example the current date time in India is Thursday, 26 July 2012, 14:27:56, time zone Kolkata. Then the time, Pacific time zone, should be Thursday, 26 July 2012, 01:59:30 PDT.
But the user changed his device time from 14:27:56 to 13:27:56, so the converted UTC time will be Thursday, 26 July 2012, 00:59:30 PDT. At this point my app is not able to get the date because of the one-hour difference.
I don't want to use date, Calendar classes for Java and I don't want to use the device time. How can I get the UTC time directly, without involving the device's time, date. Is there any open-source API for that?
Thanks in advance.
UTC Time over SntpClient above don't work if the the Sytem time changed e.g. manually for 8 hours. Because it using System.currentTimeMillis wich returns the false value!!!
// get current time and write it to the request packet
long requestTime = System.currentTimeMillis();
long requestTicks = SystemClock.elapsedRealtime();
Better use this Class to get the right UTC Time from NTP Server:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
class NTP_UTC_Time
{
private static final String TAG = "SntpClient";
private static final int RECEIVE_TIME_OFFSET = 32;
private static final int TRANSMIT_TIME_OFFSET = 40;
private static final int NTP_PACKET_SIZE = 48;
private static final int NTP_PORT = 123;
private static final int NTP_MODE_CLIENT = 3;
private static final int NTP_VERSION = 3;
// Number of seconds between Jan 1, 1900 and Jan 1, 1970
// 70 years plus 17 leap days
private static final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L;
private long mNtpTime;
public boolean requestTime(String host, int timeout) {
try {
DatagramSocket socket = new DatagramSocket();
socket.setSoTimeout(timeout);
InetAddress address = InetAddress.getByName(host);
byte[] buffer = new byte[NTP_PACKET_SIZE];
DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, NTP_PORT);
buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);
writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
socket.send(request);
// read the response
DatagramPacket response = new DatagramPacket(buffer, buffer.length);
socket.receive(response);
socket.close();
mNtpTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
} catch (Exception e) {
// if (Config.LOGD) Log.d(TAG, "request time failed: " + e);
return false;
}
return true;
}
public long getNtpTime() {
return mNtpTime;
}
/**
* Reads an unsigned 32 bit big endian number from the given offset in the buffer.
*/
private long read32(byte[] buffer, int offset) {
byte b0 = buffer[offset];
byte b1 = buffer[offset+1];
byte b2 = buffer[offset+2];
byte b3 = buffer[offset+3];
// convert signed bytes to unsigned values
int i0 = ((b0 & 0x80) == 0x80 ? (b0 & 0x7F) + 0x80 : b0);
int i1 = ((b1 & 0x80) == 0x80 ? (b1 & 0x7F) + 0x80 : b1);
int i2 = ((b2 & 0x80) == 0x80 ? (b2 & 0x7F) + 0x80 : b2);
int i3 = ((b3 & 0x80) == 0x80 ? (b3 & 0x7F) + 0x80 : b3);
return ((long)i0 << 24) + ((long)i1 << 16) + ((long)i2 << 8) + (long)i3;
}
/**
* Reads the NTP time stamp at the given offset in the buffer and returns
* it as a system time (milliseconds since January 1, 1970).
*/
private long readTimeStamp(byte[] buffer, int offset) {
long seconds = read32(buffer, offset);
long fraction = read32(buffer, offset + 4);
return ((seconds - OFFSET_1900_TO_1970) * 1000) + ((fraction * 1000L) / 0x100000000L);
}
/**
* Writes 0 as NTP starttime stamp in the buffer. --> Then NTP returns Time OFFSET since 1900
*/
private void writeTimeStamp(byte[] buffer, int offset) {
int ofs = offset++;
for (int i=ofs;i<(ofs+8);i++)
buffer[i] = (byte)(0);
}
}
And use it with:
long now = 0;
NTP_UTC_Time client = new NTP_UTC_Time();
if (client.requestTime("pool.ntp.org", 2000)) {
now = client.getNtpTime();
}
If you need UTC Time "now" as DateTimeString use function:
private String get_UTC_Datetime_from_timestamp(long timeStamp){
try{
Calendar cal = Calendar.getInstance();
TimeZone tz = cal.getTimeZone();
int tzt = tz.getOffset(System.currentTimeMillis());
timeStamp -= tzt;
// DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",Locale.getDefault());
DateFormat sdf = new SimpleDateFormat();
Date netDate = (new Date(timeStamp));
return sdf.format(netDate);
}
catch(Exception ex){
return "";
}
}
and use it with:
String UTC_DateTime = get_UTC_Datetime_from_timestamp(now);
long dateInMillis = System.currentTimeMillis();
This will return you the number of milliseconds since January 1, 1970 00:00:00 UTC.
Then just parse it to the most suitable format you want:
String format = "yyyy-MM-dd HH:mm:ss";
final SimpleDateFormat sdf = new SimpleDateFormat(format);
String dateString = sdf.format(new Date(dateInMillis)));
I think you must implement a Sntp Client and access NTP to get network time.
My code:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Date;
public class GetTime {
public static void main(String[] args) {
SntpClient client = new SntpClient();
if (client.requestTime("pool.ntp.org", 30000)) {
long now = client.getNtpTime() + System.nanoTime() / 1000
- client.getNtpTimeReference();
Date current = new Date(now);
System.out.println(current.toString());
}
}
}
class SntpClient {
private static final int ORIGINATE_TIME_OFFSET = 24;
private static final int RECEIVE_TIME_OFFSET = 32;
private static final int TRANSMIT_TIME_OFFSET = 40;
private static final int NTP_PACKET_SIZE = 48;
private static final int NTP_PORT = 123;
private static final int NTP_MODE_CLIENT = 3;
private static final int NTP_VERSION = 3;
// Number of seconds between Jan 1, 1900 and Jan 1, 1970
// 70 years plus 17 leap days
private static final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L;
// system time computed from NTP server response
private long mNtpTime;
// value of SystemClock.elapsedRealtime() corresponding to mNtpTime
private long mNtpTimeReference;
// round trip time in milliseconds
private long mRoundTripTime;
/**
* Sends an SNTP request to the given host and processes the response.
*
* #param host
* host name of the server.
* #param timeout
* network timeout in milliseconds.
* #return true if the transaction was successful.
*/
public boolean requestTime(String host, int timeout) {
try {
DatagramSocket socket = new DatagramSocket();
socket.setSoTimeout(timeout);
InetAddress address = InetAddress.getByName(host);
byte[] buffer = new byte[NTP_PACKET_SIZE];
DatagramPacket request = new DatagramPacket(buffer, buffer.length,
address, NTP_PORT);
// set mode = 3 (client) and version = 3
// mode is in low 3 bits of first byte
// version is in bits 3-5 of first byte
buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);
// get current time and write it to the request packet
long requestTime = System.currentTimeMillis();
long requestTicks = System.nanoTime() / 1000;
writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime);
socket.send(request);
// read the response
DatagramPacket response = new DatagramPacket(buffer, buffer.length);
socket.receive(response);
long responseTicks = System.nanoTime() / 1000;
long responseTime = requestTime + (responseTicks - requestTicks);
socket.close();
// extract the results
long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET);
long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
long roundTripTime = responseTicks - requestTicks
- (transmitTime - receiveTime);
// receiveTime = originateTime + transit + skew
// responseTime = transmitTime + transit - skew
// clockOffset = ((receiveTime - originateTime) + (transmitTime -
// responseTime))/2
// = ((originateTime + transit + skew - originateTime) +
// (transmitTime - (transmitTime + transit - skew)))/2
// = ((transit + skew) + (transmitTime - transmitTime - transit +
// skew))/2
// = (transit + skew - transit + skew)/2
// = (2 * skew)/2 = skew
long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime)) / 2;
// if (Config.LOGD) Log.d(TAG, "round trip: " + roundTripTime +
// " ms");
// if (Config.LOGD) Log.d(TAG, "clock offset: " + clockOffset +
// " ms");
// save our results - use the times on this side of the network
// latency
// (response rather than request time)
mNtpTime = responseTime + clockOffset;
mNtpTimeReference = responseTicks;
mRoundTripTime = roundTripTime;
} catch (Exception e) {
return false;
}
return true;
}
/**
* Returns the time computed from the NTP transaction.
*
* #return time value computed from NTP server response.
*/
public long getNtpTime() {
return mNtpTime;
}
/**
* Returns the reference clock value (value of
* SystemClock.elapsedRealtime()) corresponding to the NTP time.
*
* #return reference clock corresponding to the NTP time.
*/
public long getNtpTimeReference() {
return mNtpTimeReference;
}
/**
* Returns the round trip time of the NTP transaction
*
* #return round trip time in milliseconds.
*/
public long getRoundTripTime() {
return mRoundTripTime;
}
/**
* Reads an unsigned 32 bit big endian number from the given offset in the
* buffer.
*/
private long read32(byte[] buffer, int offset) {
byte b0 = buffer[offset];
byte b1 = buffer[offset + 1];
byte b2 = buffer[offset + 2];
byte b3 = buffer[offset + 3];
// convert signed bytes to unsigned values
int i0 = ((b0 & 0x80) == 0x80 ? (b0 & 0x7F) + 0x80 : b0);
int i1 = ((b1 & 0x80) == 0x80 ? (b1 & 0x7F) + 0x80 : b1);
int i2 = ((b2 & 0x80) == 0x80 ? (b2 & 0x7F) + 0x80 : b2);
int i3 = ((b3 & 0x80) == 0x80 ? (b3 & 0x7F) + 0x80 : b3);
return ((long) i0 << 24) + ((long) i1 << 16) + ((long) i2 << 8)
+ (long) i3;
}
/**
* Reads the NTP time stamp at the given offset in the buffer and returns it
* as a system time (milliseconds since January 1, 1970).
*/
private long readTimeStamp(byte[] buffer, int offset) {
long seconds = read32(buffer, offset);
long fraction = read32(buffer, offset + 4);
return ((seconds - OFFSET_1900_TO_1970) * 1000)
+ ((fraction * 1000L) / 0x100000000L);
}
/**
* Writes system time (milliseconds since January 1, 1970) as an NTP time
* stamp at the given offset in the buffer.
*/
private void writeTimeStamp(byte[] buffer, int offset, long time) {
long seconds = time / 1000L;
long milliseconds = time - seconds * 1000L;
seconds += OFFSET_1900_TO_1970;
// write seconds in big endian format
buffer[offset++] = (byte) (seconds >> 24);
buffer[offset++] = (byte) (seconds >> 16);
buffer[offset++] = (byte) (seconds >> 8);
buffer[offset++] = (byte) (seconds >> 0);
long fraction = milliseconds * 0x100000000L / 1000L;
// write fraction in big endian format
buffer[offset++] = (byte) (fraction >> 24);
buffer[offset++] = (byte) (fraction >> 16);
buffer[offset++] = (byte) (fraction >> 8);
// low order bits should be random data
buffer[offset++] = (byte) (Math.random() * 255.0);
}
}
Hope that can help you.
Using the free API at http://www.worldweatheronline.com/, you can perform a HTTP GET via java to get the current time in UTC (http://www.worldweatheronline.com/time-zone-api.aspx)
For android, if you tried to connect to the Internet from the main thread the app will crash, so I edited to code to work correctly on android
import android.os.AsyncTask;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class NTPUTCTime {
private static final String TAG = "SntpClient";
private static final int RECEIVE_TIME_OFFSET = 32;
private static final int TRANSMIT_TIME_OFFSET = 40;
private static final int NTP_PACKET_SIZE = 48;
private static final int NTP_PORT = 123;
private static final int NTP_MODE_CLIENT = 3;
private static final int NTP_VERSION = 3;
// Number of seconds between Jan 1, 1900 and Jan 1, 1970
// 70 years plus 17 leap days
private static final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L;
private long mNtpTime;
public boolean requestTime() {
try {
new AsyncTask<Void, Void, Boolean>() {
#Override
protected Boolean doInBackground(Void... voids) {
try {
DatagramSocket socket = new DatagramSocket();
socket.setSoTimeout(1000);
InetAddress address = InetAddress.getByName("pool.ntp.org");
byte[] buffer = new byte[NTP_PACKET_SIZE];
DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, NTP_PORT);
buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);
writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
socket.send(request);
// read the response
DatagramPacket response = new DatagramPacket(buffer, buffer.length);
socket.receive(response);
socket.close();
mNtpTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
} catch (Exception e) {
// if (Config.LOGD) Log.d(TAG, "request time failed: " + e);
e.printStackTrace();
return false;
}
return true;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR).get();
} catch (Exception e) {
// if (Config.LOGD) Log.d(TAG, "request time failed: " + e);
e.printStackTrace();
return false;
}
return true;
}
public long getNtpTime() {
return mNtpTime;
}
/**
* Reads an unsigned 32 bit big endian number from the given offset in the buffer.
*/
private long read32(byte[] buffer, int offset) {
byte b0 = buffer[offset];
byte b1 = buffer[offset + 1];
byte b2 = buffer[offset + 2];
byte b3 = buffer[offset + 3];
// convert signed bytes to unsigned values
int i0 = ((b0 & 0x80) == 0x80 ? (b0 & 0x7F) + 0x80 : b0);
int i1 = ((b1 & 0x80) == 0x80 ? (b1 & 0x7F) + 0x80 : b1);
int i2 = ((b2 & 0x80) == 0x80 ? (b2 & 0x7F) + 0x80 : b2);
int i3 = ((b3 & 0x80) == 0x80 ? (b3 & 0x7F) + 0x80 : b3);
return ((long) i0 << 24) + ((long) i1 << 16) + ((long) i2 << 8) + (long) i3;
}
/**
* Reads the NTP time stamp at the given offset in the buffer and returns
* it as a system time (milliseconds since January 1, 1970).
*/
private long readTimeStamp(byte[] buffer, int offset) {
long seconds = read32(buffer, offset);
long fraction = read32(buffer, offset + 4);
return ((seconds - OFFSET_1900_TO_1970) * 1000) + ((fraction * 1000L) / 0x100000000L);
}
/**
* Writes 0 as NTP starttime stamp in the buffer. --> Then NTP returns Time OFFSET since 1900
*/
private void writeTimeStamp(byte[] buffer, int offset) {
int ofs = offset++;
for (int i = ofs; i < (ofs + 8); i++)
buffer[i] = (byte) (0);
}
}
You can use Joda library to manipulate date/time data
class GetUTCTime : AppCompatActivity() {
private val OUTPUT_DATE_FORMATE = "dd-MM-yyyy - hh:mm a"
internal lateinit var textView : TextView
val utcTime: String
get() {
LongOperation().execute("")
return ""
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_get_utctime)
// Log.d("UTC", "onCreate: "+ getUTCTime());
textView = findViewById(R.id.dateTime)
findViewById<View>(R.id.fetchButton).setOnClickListener {
utcTime
textView.text = "Fetching..."
}
}
private inner class LongOperation : AsyncTask<String, Void, Long>() {
override fun doInBackground(vararg params: String): Long? {
var nowAsPerDeviceTimeZone: Long = 0
try {
val sntpClient = SntpClient()
if (sntpClient.requestTime("ntp.ubuntu.com", 30000)) {
nowAsPerDeviceTimeZone = sntpClient.ntpTime
}
Thread.sleep(1000)
} catch (e: InterruptedException) {
Thread.interrupted()
}
return nowAsPerDeviceTimeZone
}
override fun onPostExecute(nowAsPerDeviceTimeZone: Long?) {
// might want to change "executed" for the returned string passed
// into onPostExecute() but that is upto you
Log.d("GMT", "getUTCTime:0 " + Date(nowAsPerDeviceTimeZone!!))
textView.text = Date(nowAsPerDeviceTimeZone).toString()
// Calendar cal = Calendar.getInstance();
// TimeZone timeZoneInDevice = cal.getTimeZone();
// int differentialOfTimeZones = timeZoneInDevice.getOffset(System.currentTimeMillis());
// nowAsPerDeviceTimeZone -= differentialOfTimeZones;
//
//
//
//
//
// Log.d("GMT", "getUTCTime:1 "+ new Date(nowAsPerDeviceTimeZone).toString());
}
override fun onPreExecute() {}
override fun onProgressUpdate(vararg values: Void) {}
}
}
Find the SntpClient here
Related
I want to print image along with some other texts through zebra printer from android application. I am able to create zpl code for text data but I am having to problem to create zpl code for image. The zpl do not supports base64 code. The image needs to be converted to hex ascii and print using ^GF command.
The raw data with both text and image is available on this Pastebin link, and can be viewed in labelary viewer.
Is there any image conversion process?
I solved the problem and I am posting the answer so that other people can take benefit from the solution. The bitmap image can be converted to zpl code using following converter class.
public class ZPLConverter {
private int blackLimit = 380;
private int total;
private int widthBytes;
private boolean compressHex = false;
private static Map<Integer, String> mapCode = new HashMap<Integer, String>();
{
mapCode.put(1, "G");
mapCode.put(2, "H");
mapCode.put(3, "I");
mapCode.put(4, "J");
mapCode.put(5, "K");
mapCode.put(6, "L");
mapCode.put(7, "M");
mapCode.put(8, "N");
mapCode.put(9, "O");
mapCode.put(10, "P");
mapCode.put(11, "Q");
mapCode.put(12, "R");
mapCode.put(13, "S");
mapCode.put(14, "T");
mapCode.put(15, "U");
mapCode.put(16, "V");
mapCode.put(17, "W");
mapCode.put(18, "X");
mapCode.put(19, "Y");
mapCode.put(20, "g");
mapCode.put(40, "h");
mapCode.put(60, "i");
mapCode.put(80, "j");
mapCode.put(100, "k");
mapCode.put(120, "l");
mapCode.put(140, "m");
mapCode.put(160, "n");
mapCode.put(180, "o");
mapCode.put(200, "p");
mapCode.put(220, "q");
mapCode.put(240, "r");
mapCode.put(260, "s");
mapCode.put(280, "t");
mapCode.put(300, "u");
mapCode.put(320, "v");
mapCode.put(340, "w");
mapCode.put(360, "x");
mapCode.put(380, "y");
mapCode.put(400, "z");
}
public String convertFromImage(Bitmap image, Boolean addHeaderFooter) {
String hexAscii = createBody(image);
if (compressHex) {
hexAscii = encodeHexAscii(hexAscii);
}
String zplCode = "^GFA," + total + "," + total + "," + widthBytes + ", " + hexAscii;
if (addHeaderFooter) {
String header = "^XA " + "^FO0,0^GFA," + total + "," + total + "," + widthBytes + ", ";
String footer = "^FS" + "^XZ";
zplCode = header + zplCode + footer;
}
return zplCode;
}
private String createBody(Bitmap bitmapImage) {
StringBuilder sb = new StringBuilder();
int height = bitmapImage.getHeight();
int width = bitmapImage.getWidth();
int rgb, red, green, blue, index = 0;
char auxBinaryChar[] = {'0', '0', '0', '0', '0', '0', '0', '0'};
widthBytes = width / 8;
if (width % 8 > 0) {
widthBytes = (((int) (width / 8)) + 1);
} else {
widthBytes = width / 8;
}
this.total = widthBytes * height;
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
rgb = bitmapImage.getPixel(w, h);
red = (rgb >> 16) & 0x000000FF;
green = (rgb >> 8) & 0x000000FF;
blue = (rgb) & 0x000000FF;
char auxChar = '1';
int totalColor = red + green + blue;
if (totalColor > blackLimit) {
auxChar = '0';
}
auxBinaryChar[index] = auxChar;
index++;
if (index == 8 || w == (width - 1)) {
sb.append(fourByteBinary(new String(auxBinaryChar)));
auxBinaryChar = new char[]{'0', '0', '0', '0', '0', '0', '0', '0'};
index = 0;
}
}
sb.append("\n");
}
return sb.toString();
}
private String fourByteBinary(String binaryStr) {
int decimal = Integer.parseInt(binaryStr, 2);
if (decimal > 15) {
return Integer.toString(decimal, 16).toUpperCase();
} else {
return "0" + Integer.toString(decimal, 16).toUpperCase();
}
}
private String encodeHexAscii(String code) {
int maxlinea = widthBytes * 2;
StringBuilder sbCode = new StringBuilder();
StringBuilder sbLinea = new StringBuilder();
String previousLine = null;
int counter = 1;
char aux = code.charAt(0);
boolean firstChar = false;
for (int i = 1; i < code.length(); i++) {
if (firstChar) {
aux = code.charAt(i);
firstChar = false;
continue;
}
if (code.charAt(i) == '\n') {
if (counter >= maxlinea && aux == '0') {
sbLinea.append(",");
} else if (counter >= maxlinea && aux == 'F') {
sbLinea.append("!");
} else if (counter > 20) {
int multi20 = (counter / 20) * 20;
int resto20 = (counter % 20);
sbLinea.append(mapCode.get(multi20));
if (resto20 != 0) {
sbLinea.append(mapCode.get(resto20)).append(aux);
} else {
sbLinea.append(aux);
}
} else {
sbLinea.append(mapCode.get(counter)).append(aux);
}
counter = 1;
firstChar = true;
if (sbLinea.toString().equals(previousLine)) {
sbCode.append(":");
} else {
sbCode.append(sbLinea.toString());
}
previousLine = sbLinea.toString();
sbLinea.setLength(0);
continue;
}
if (aux == code.charAt(i)) {
counter++;
} else {
if (counter > 20) {
int multi20 = (counter / 20) * 20;
int resto20 = (counter % 20);
sbLinea.append(mapCode.get(multi20));
if (resto20 != 0) {
sbLinea.append(mapCode.get(resto20)).append(aux);
} else {
sbLinea.append(aux);
}
} else {
sbLinea.append(mapCode.get(counter)).append(aux);
}
counter = 1;
aux = code.charAt(i);
}
}
return sbCode.toString();
}
public void setCompressHex(boolean compressHex) {
this.compressHex = compressHex;
}
public void setBlacknessLimitPercentage(int percentage) {
blackLimit = (percentage * 768 / 100);
}
}
Example usage:
You need to convert the your image to bitmap, convert to monochrome image and do hex acii conversion. The generated zpl code can be checked at labelary viewer.
public class Utils {
public static String getZplCode(Bitmap bitmap, Boolean addHeaderFooter) {
ZPLConverter zp = new ZPLConverter();
zp.setCompressHex(true);
zp.setBlacknessLimitPercentage(50);
Bitmap grayBitmap = toGrayScale(bitmap);
return zp.convertFromImage(grayBitmap, addHeaderFooter);
}
public static Bitmap toGrayScale(Bitmap bmpOriginal) {
int width, height;
height = bmpOriginal.getHeight();
width = bmpOriginal.getWidth();
Bitmap grayScale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(grayScale);
Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
cm.setSaturation(0);
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
paint.setColorFilter(f);
c.drawBitmap(bmpOriginal, 0, 0, paint);
return grayScale;
}
}
The converter code has been referenced from here and added support for android use.
I know this is kind of done deal but I still struggle a lot with the current answer. I wanted to share my experience for those who might need it.
First of all,^GFA stands for hexadecial representation of the pixel but it has to be converted into readable text (ASCII). Here is an example :
Pixels white = 1, black = 1
1011 0100 translate into 0xB4
In the data section of ^GFA, you need to have B4 as data.
If we go with
Pixel line 1 : 1011 0100 1001 1100 = 0xBA 0x9C
Pixel line 2 : 0011 0110 0001 1111 = 0x36 0x1F
the resulting ZPL code will be :
^XA (needed to start a ZPL file)
^F10,0 (offsets 10 pixel horizontal, 0 pixel vertical)
^GFA,4,4,2,BA9C361F (4 is the total number of byte, 2 is the number of byte per line)
^F0^XZ (end of file)
Now, the interesting point. How to get to code that :
You need a gray-scale bitmap. You need pixel-wise access to the bitmap. In another words, an array containing integer which values varies from 0 to 255.
With that array you take each bunch of 8 pixels, convert it to hexadecimal value and then to text representation of these hexadecimals. Here is the c++ code made with Borland :
Graphics::TBitmap *imageFax = new Graphics::TBitmap();
unsigned char r;
unsigned char b;
ofstream outFile;
char listeHex[16];
int lineByteWidth;
int j;
int bytesCount = 0;
int widthHeight;
AnsiString testOut;
listeHex[0] = '0';
listeHex[1] = '1';
listeHex[2] = '2';
listeHex[3] = '3';
listeHex[4] = '4';
listeHex[5] = '5';
listeHex[6] = '6';
listeHex[7] = '7';
listeHex[8] = '8';
listeHex[9] = '9';
listeHex[10] = 'A';
listeHex[11] = 'B';
listeHex[12] = 'C';
listeHex[13] = 'D';
listeHex[14] = 'E';
listeHex[15] = 'F';
imageFax->Monochrome = true;
imageFax->PixelFormat = pf8bit;
imageFax->LoadFromFile("c:/testEtiquette/test.bmp"); //1200x300pixels bitmap test image
testOut = "c:/testEtiquette/outputfile.txt";
outFile.open(testOut.c_str());
imageFax->PixelFormat = pf8bit;
lineByteWidth = imageFax->Width/8;//Number of byte per line
widthHeight = lineByteWidth*imageFax->Height;//number of total byte to be written into the output file
testOut = "^XA^FO10,0^GFA,";
outFile << testOut.c_str() << widthHeight << ',' << widthHeight << ',' << lineByteWidth << ',' ;
for(int i = 0; i < imageFax->Height; i++)
{
unsigned char * pixel = (unsigned char *)imageFax->ScanLine[i];
bytesCount = 0;
b=0x00;
for(j = 0; j < imageFax->Width; j++)
{
//Here is the "switch" : what is not white (255) bit = 0, is black bit = 1.
//You can set your switch at whatever value you think is best. 0, 255 or anything between.
//I think 255 (white) is a good for my application
if(pixel[j] != 255)
{
b = b<<1;
//It is not white (hence black), we force value 1 into current position
b = b|0x01;
}
else
{
//Since it white, we move 1 bit to the left, pushing 0 into current position
b = b<<1;
b = b&0xFE;//Forcing a 0 in the current position
}
//If we've got a full byte (8-bits), we write it into the file
//This will lead into cutting off part of images that width is not a multiple of 8
if(j%8 == 7)
{
bytesCount++;
r = b;
r = r&0xF0; //Cleaning last digits
r=r>>4; //Moving the bits to the left 0xF0 => 0x0F
outFile << listeHex[r%16]; //Reaching into the conversion array listeHex, ASCII representation of hex value
r = listeHex[r%16]; //For debug only
r = b;
r = r&0x0F;//Cleaning first digits
outFile << listeHex[r%16];//Reaching into the conversion array listeHex, ASCII representation of hex value
r = listeHex[r%16]; //For debug only
b = 0x00; //Reseting for next Byte
}
}
}
testOut = "^F0^XZ";
outFile << testOut.c_str();
outFile.close();
delete imageFax;
Some links :
ZPL PDF doc (see page 191 for Graphic conversion) https://www.zebra.com/content/dam/zebra/manuals/printers/common/programming/zpl-zbi2-pm-en.pdf
(If link does not work, try "zpl-zbi2-pm-en.pdf" on google)
https://www.rapidtables.com/convert/number/binary-to-hex.html
here is the complete working code of IP printer (Model GK420t ZPL And you can access any IP printer). Just replace only three things 1) Add you IP address 2) add your port number 3) Add you PNG File path
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Management;
using System.Net.Http;
using System.ServiceModel.Channels;
using System.Web;
using System.Web.Http;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.IO;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Printing;
using System.Net.NetworkInformation;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing.Printing;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
string ipAddress = "Your IP address";
int port = Your port number;
string zplImageData = string.Empty;
string filePath = #"your png file path";
byte[] binaryData = System.IO.File.ReadAllBytes(filePath);
foreach (Byte b in binaryData)
{
string hexRep = String.Format("{0:X}", b);
if (hexRep.Length == 1)
hexRep = "0" + hexRep;
zplImageData += hexRep;
}
string zplToSend = "^XA" + "^FO50" + "50^GFA,120000,120000,100" + binaryData.Length + ",," + zplImageData + "^XZ";
string printImage = "^XA^FO115,50^IME:LOGO.PNG^FS^XZ";
try
{
// Open connection
System.Net.Sockets.TcpClient client = new System.Net.Sockets.TcpClient();
client.Connect(ipAddress, port);
// Write ZPL String to connection
System.IO.StreamWriter writer = new System.IO.StreamWriter(client.GetStream(), Encoding.UTF8);
writer.Write(zplToSend);
writer.Flush();
writer.Write(printImage);
writer.Flush();
// Close Connection
writer.Close();
client.Close();
}
catch (Exception ex)
{
// Catch Exception
}
}
}
}
I am developing an application which does image processing in real time to get the y-sum and time between each frame. They are stored in 2 double arrays and for further implementations i need to run Fast Fourier Transformation on these values.
I have seen several fft algorithms in other questions such as
Stack Overflow Question 1
Stack Overflow Question 2
I have also read topics where they suggesting using JTransform library
However, Since I have very very limited knowledge about FFT, I am not sure how to implement it into my code.
My MainActivity is this
public class MainActivity extends AppCompatActivity implements CameraView.PreviewReadyCallback {
private static Camera camera = null;
private CameraView image = null;
Button fftButton;
private LineChart bp_graph;
private int img_Y_Avg, img_U_Avg, img_V_Avg;
private long end = 0, begin = 0;
Handler handler;
private int readingRemaining = 1200;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
bp_graph = (LineChart)findViewById(R.id.graph);
fftButton = (Button)findViewById(R.id.runFFT);
graph_features();
//open camera
try {
camera = Camera.open();
handler = new Handler();
final Runnable runnable = new Runnable() {
#Override
public void run() {
camera.stopPreview();
camera.release();
fftButton.setVisibility(View.VISIBLE);
}
};
handler.postDelayed(runnable, 30000);
} catch (Exception e) {
Log.d("ERROR", "Failed to get camera: " + e.getMessage());
}
if (camera != null) {
image = new CameraView(this, camera);
FrameLayout camera_view = (FrameLayout) findViewById(R.id.camera_view);
camera_view.addView(image);
image.setOnPreviewReady(this);
}
}
#Override
protected void onResume(){
super.onResume();
}
#Override
protected void onPause() {
super.onPause();
}
#Override
public void onPreviewFrame(long startTime, int ySum, int uSum, int vSum, long endTime) {
begin = startTime;
img_Y_Avg = ySum;
img_U_Avg = uSum;
img_V_Avg = vSum;
end = endTime;
showResults(begin, img_Y_Avg, img_U_Avg, img_V_Avg, end);
}
private void showResults(long startTime, int ySum, int uSum, int vSum, long endTime){
//set value of Y on the text view
TextView valueOfY = (TextView)findViewById(R.id.valueY);
//valueY = img_Y_Avg;
valueOfY.setText(String.valueOf(img_Y_Avg));
//start time in milliseconds
long StartDurationInMs = TimeUnit.MILLISECONDS.convert(begin, TimeUnit.MILLISECONDS);
ArrayList<Long> startOfTime = new ArrayList<>();
startOfTime.add(StartDurationInMs);
//store value to array list
ArrayList<Integer> yAverage = new ArrayList<>();
yAverage.add(img_Y_Avg);
ArrayList<Long> getValues = new ArrayList<>();
for(int i = 0; i < yAverage.size(); i++) {
getValues.add(startOfTime.get(i));
getValues.add((long)(yAverage.get(i)));
}
storeCsv(yAverage, getValues);
Log.d("MyEntryData", String.valueOf(getValues));
}
/**
* method to store raw time and y-sum data into CSV file**/
public void storeCsv(ArrayList<Integer>yAverage, ArrayList<Long>getValues){
String filename = "temporary.csv";
//File directoryDownload = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/bpReader";
//File logDir = new File (directoryDownload, "bpReader"); //Creates a new folder in DOWNLOAD directory
File logDir = new File(path);
logDir.mkdirs();
File file = new File(logDir, filename);
FileOutputStream outputStream = null;
try {
file.createNewFile();
outputStream = new FileOutputStream(file, true);
//outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
for (int i = 0; i < yAverage.size(); i += 2) {
outputStream.write((getValues.get(i) + ",").getBytes());
outputStream.write((getValues.get(i + 1) + "\n").getBytes());
//outputStream.write((getValues.get(i + 2) + ",").getBytes());
//outputStream.write((getValues.get(i + 3) + "\n").getBytes());
}
outputStream.flush();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//Method for button which appears after the reading is done and the data is saved into csv file
public void readFile(View view){
readCsv();
}
//Method to read the data from the csv and get the time and y-sum value
public void readCsv(){
String getPath = Environment.getExternalStorageDirectory() + "/bpReader";
String csvFile = "temporary.csv";
String path = getPath+ "/" + csvFile;
int length = 500;
double[] xCoords = new double[length];
double[] yCoords = new double[length];
double[] newXcord = new double[length];
CSVReader reader;
try {
File myFile = new File (path);
reader = new CSVReader(new FileReader(myFile));
String[] line;
int i;
for (i = 0; i < xCoords.length; i ++){
if ((line = reader.readNext()) != null){
xCoords[i] = Double.parseDouble(line[0]);
yCoords[i] = Double.parseDouble(line[1]);
}
}
for (i = 0; i < xCoords.length ; i++){
if (xCoords[i]!=0) {
newXcord[i] = xCoords[i] - xCoords[0];
Log.d("read:: ", "Time: " + String.valueOf(newXcord[i]) + " Y-Sum " + String.valueOf(yCoords[i]));
}
}
myFile.delete();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
After I have read the CSV file in readCsv() method, i get data like this
10-15 16:37:50.127 16912-16912/redlight55.com.bpreader D/read::: Time: 0.0 Y-Sum 570194.0
10-15 16:37:50.127 16912-16912/redlight55.com.bpreader D/read::: Time: 50.0 Y-Sum 405504.0
10-15 16:37:50.127 16912-16912/redlight55.com.bpreader D/read::: Time: 118.0 Y-Sum 405504.0
10-15 16:37:50.127 16912-16912/redlight55.com.bpreader D/read::: Time: 168.0 Y-Sum 405504.0
10-15 16:37:50.127 16912-16912/redlight55.com.bpreader D/read::: Time: 234.0 Y-Sum 429242.0
10-15 16:37:50.127 16912-16912/redlight55.com.bpreader D/read::: Time: 301.0 Y-Sum 1217635.0
10-15 16:37:50.127 16912-16912/redlight55.com.bpreader D/read::: Time: 368.0 Y-Sum 1516666.0
10-15 16:37:50.127 16912-16912/redlight55.com.bpreader D/read::: Time: 418.0 Y-Sum 1495037.0
10-15 16:37:50.127 16912-16912/redlight55.com.bpreader D/read::: Time: 486.0 Y-Sum 1514453.0
10-15 16:37:50.128 16912-16912/redlight55.com.bpreader D/read::: Time: 553.0 Y-Sum 1507075.0
10-15 16:37:50.128 16912-16912/redlight55.com.bpreader D/read::: Time: 625.0 Y-Sum 1511241.0
10-15 16:37:50.128 16912-16912/redlight55.com.bpreader D/read::: Time: 678.0 Y-Sum 1476090.0
10-15 16:37:50.128 16912-16912/redlight55.com.bpreader D/read::: Time: 749.0 Y-Sum 1476961.0
10-15 16:37:50.128 16912-16912/redlight55.com.bpreader D/read::: Time: 815.0 Y-Sum 1470232.0
My FFT class is this which i got from some of the suggestions in other stack overflow questions.
public class FFT {
/**
* The Fast Fourier Transform (generic version, with NO optimizations).
*
* #param inputReal
* an array of length n, the real part
* #param inputImag
* an array of length n, the imaginary part
* #param DIRECT
* TRUE = direct transform, FALSE = inverse transform
* #return a new array of length 2n
*/
public static double[] fft(final double[] inputReal, double[] inputImag,
boolean DIRECT) {
// - n is the dimension of the problem
// - nu is its logarithm in base e
int n = inputReal.length;
// If n is a power of 2, then ld is an integer (_without_ decimals)
double ld = Math.log(n) / Math.log(2.0);
// Here I check if n is a power of 2. If exist decimals in ld, I quit
// from the function returning null.
if (((int) ld) - ld != 0) {
System.out.println("The number of elements is not a power of 2.");
return null;
}
// Declaration and initialization of the variables
// ld should be an integer, actually, so I don't lose any information in
// the cast
int nu = (int) ld;
int n2 = n / 2;
int nu1 = nu - 1;
double[] xReal = new double[n];
double[] xImag = new double[n];
double tReal, tImag, p, arg, c, s;
// Here I check if I'm going to do the direct transform or the inverse
// transform.
double constant;
if (DIRECT)
constant = -2 * Math.PI;
else
constant = 2 * Math.PI;
// I don't want to overwrite the input arrays, so here I copy them. This
// choice adds \Theta(2n) to the complexity.
for (int i = 0; i < n; i++) {
xReal[i] = inputReal[i];
xImag[i] = inputImag[i];
}
// First phase - calculation
int k = 0;
for (int l = 1; l <= nu; l++) {
while (k < n) {
for (int i = 1; i <= n2; i++) {
p = bitReverseReference(k >> nu1, nu);
// direct FFT or inverse FFT
arg = constant * p / n;
c = Math.cos(arg);
s = Math.sin(arg);
tReal = xReal[k + n2] * c + xImag[k + n2] * s;
tImag = xImag[k + n2] * c - xReal[k + n2] * s;
xReal[k + n2] = xReal[k] - tReal;
xImag[k + n2] = xImag[k] - tImag;
xReal[k] += tReal;
xImag[k] += tImag;
k++;
}
k += n2;
}
k = 0;
nu1--;
n2 /= 2;
}
// Second phase - recombination
k = 0;
int r;
while (k < n) {
r = bitReverseReference(k, nu);
if (r > k) {
tReal = xReal[k];
tImag = xImag[k];
xReal[k] = xReal[r];
xImag[k] = xImag[r];
xReal[r] = tReal;
xImag[r] = tImag;
}
k++;
}
// Here I have to mix xReal and xImag to have an array (yes, it should
// be possible to do this stuff in the earlier parts of the code, but
// it's here to readibility).
double[] newArray = new double[xReal.length * 2];
double radice = 1 / Math.sqrt(n);
for (int i = 0; i < newArray.length; i += 2) {
int i2 = i / 2;
// I used Stephen Wolfram's Mathematica as a reference so I'm going
// to normalize the output while I'm copying the elements.
newArray[i] = xReal[i2] * radice;
newArray[i + 1] = xImag[i2] * radice;
}
return newArray;
}
/**
* The reference bitreverse function.
*/
private static int bitReverseReference(int j, int nu) {
int j2;
int j1 = j;
int k = 0;
for (int i = 1; i <= nu; i++) {
j2 = j1 / 2;
k = 2 * k + j1 - 2 * j2;
j1 = j2;
}
return k;
}
}
My Question is
Can I implement this FFT class in my code.
If, yes can anyone direct me on how to do it? I guess i have to perform the fft inside the readCsv method and use the values as input once have read all the values.
I am intending to perform FSK demodulation and came across the Androino Project(https://code.google.com/p/androino/source/browse/wiki/AndroinoTerminal.wiki), which reads in data from the Adruino into the phone's audio jack, which is pretty darn cool.
I am trying to go through the code but I can't make sense of some impt values. :((
Why is the bit-high = 22 peaks, bit-low = 6 peaks, private static int HIGH_BIT_N_PEAKS = 12 and private static int LOW_BIT_N_PEAKS = 7?? And why is it 136 samples per encoded bit?
Am I also right to say that the FSK rate of the Adruino is set at 315Hz?
I have attached the hardware(softTerm) codes as well: https://code.google.com/p/androino/source/browse/trunk/arduino/SoftTerm/SoftModem/SoftModem.h and the cpp file is in there as well. Dun have enough reputation points to post both links.
/** Copyright (C) 2011 Androino authors
Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.androino.ttt;
import android.util.Log;
public class FSKModule {
// Experimental results
// Arduino sends "a" character (97) 1100001
// The generated message is 0110.0001
// 136 samples per encoded bit
// total message = 166 bits: 155(high)+1(low)+8bit+stop(high)+end(high)
private static int SAMPLING_FREQUENCY = 44100; //Hz
private static double SAMPLING_TIME = 1.0/SAMPLING_FREQUENCY; //ms
// reading zero+155(high)+1(low)+8bit+stop+end+zero
private static int FREQUENCY_HIGH = 3150;
private static int FREQUENCY_LOW = 1575;
//high: 7 samples/peak
//low : 14 samples/peak
// 1492 samples/message low+8bits+stop+end
// 136 samples/bit (=1492/11)
private static int SAMPLES_PER_BIT = 136;
private static int ENCODING_SAMPLES_PER_BIT = SAMPLES_PER_BIT/2; // 68
// bit-high = 22 peaks
// bit-low = 6 peaks
private static int HIGH_BIT_N_PEAKS = 12;
private static int LOW_BIT_N_PEAKS = 7;
private static int SLOTS_PER_BIT = 4; // 4 parts: determines the size of the part analyzed to count peaks
private static int N_POINTS = SAMPLES_PER_BIT/SLOTS_PER_BIT; // 34=136/4
private static double PEAK_AMPLITUDE_TRESHOLD = 60; // significant sample (not noise)
private static int NUMBER_SAMPLES_PEAK = 4; // minimum number of significant samples to be considered a peak
private static int MINUMUM_NPEAKS = 100; // if lower it means that there is no signal/message
private static final int BIT_HIGH_SYMBOL=2;
private static final int BIT_LOW_SYMBOL=1;
private static final int BIT_NONE_SYMBOL=0;
private static final int CARRIER_MIN_HIGH_BITS=12;
private static final int SOUND_AMPLITUDE = 31000;
private static final String TAG = "FSKModule";
private FSKModule(){
}
private static void debugInfo(String message){
//System.out.println(">>" + message);
Log.w(TAG, "FSKDEC:"+ message);
}
//-----------------------------------------
// DECODING FUNCTIONS
//-----------------------------------------
public static boolean signalAvailable(double[] sound){
FSKModule m = new FSKModule();
int nPoints = N_POINTS;
int nParts = sound.length / nPoints;
int nPeaks = 0;
int startIndex = 0;
int i = 0;
do {
int endIndex = startIndex + nPoints;
int n = m.countPeaks(sound, startIndex, endIndex);
nPeaks += n;
i++;
startIndex = endIndex;
if (nPeaks > MINUMUM_NPEAKS) return true;
} while (i<nParts);
if (nPeaks >3)
debugInfo("signalAvailable() nPeaks=" + nPeaks);
return false;
}
public static int decodeSound(double[] sound){
FSKModule m = new FSKModule();
// processing sound in parts and
//Log.w(TAG, "ENTRO EN processSound");
int[] nPeaks = m.processSound(sound);
if (nPeaks.length == 0) // exit: no signal detected
return -1;
//debugInfo("decodeSound nPeaks=" + nPeaks.length);
// transform number of peaks into bits
//Log.w(TAG, "ENTRO EN parseBits");
int[] bits = m.parseBits(nPeaks);//-------------------------> OK!!
//debugInfo("decodeSound nBits=" + bits.length);
// extract message from the bit array
int message = m.decodeUniqueMessage(bits, sound, nPeaks);
debugInfo("decodeSound(): message="+message + ":" + Integer.toBinaryString(message));
return message;
}
private int decodeUniqueMessageCorrected(int[] nPeaks, int startBit){
int message = 0;
// process nPeaks starting from the end
int index = (startBit+12)*SLOTS_PER_BIT;
// find zero -> non zero transition
for (int i = 0; i < index; i++) {
int i2 = nPeaks[index-i];
int i1 = nPeaks[index-i-1];
debugInfo("zero->nonzero index=" + (index-i) + ": i2=" + i2 + ":i1=" + i1);
if ( (i1-i2)>2) {
index = index-i-1;
break;
}
}
debugInfo("zero->nonzero index=" + index);
int[] bits = new int[2+8+1+2];
for (int i = 0; i < bits.length; i++) {
int peakCounter = 0;
for (int j = 0; j < 4; j++) {
peakCounter += nPeaks[index-j];
}
debugInfo("decode corrected: peakCounter="+i + ":" + peakCounter);
if (peakCounter > 7) { //LOW_BIT_N_PEAKS)
bits[i] = BIT_LOW_SYMBOL;
}
if (peakCounter > 12) { //LOW_BIT_N_PEAKS)
bits[i] = BIT_HIGH_SYMBOL;
message += Math.pow(2, i);
}
debugInfo("bit=" + bits[i] + ":" + message);
index = index -4;
}
debugInfo("decode corrected: message="+message + ":" + Integer.toBinaryString(message));
message = 0;
for (int i = 2; i < 10; i++) {
if ( bits[i] == BIT_HIGH_SYMBOL) {
message+= Math.pow(2, 7-(i-2));
}
}
return message;
}
private int decodeUniqueMessage(int[] bits, double[] sound, int[] nPeaks){
// start bit
int index = findStartBit(bits, 0);
debugInfo("decodeUniqueMessage():start bit=" + index);
if (index == -1) return -1; // no start-bit detected
if (index + 8 + 2 > bits.length)
throw new AndroinoException("Message cutted, start bit at " + index, AndroinoException.TYPE_FSK_DECODING_ERROR);
// debugging information
int number = 16; // n bits to debug
for (int i = index-5; i < index-5+number; i++) {
debugInfo("decodeUniqueMessage(): bits=" + i +":" + bits[i] );
}
for (int i = 0; i < number*SLOTS_PER_BIT; i++) {
int position = i + (index-5)*SLOTS_PER_BIT ;
debugInfo("decodeUniqueMessage(): npeaks=" + position+ ":" + nPeaks[position] );
}
// 8bits message
int value = 0;
for (int i = 0; i < 8; i++) {
int bit = bits[index+i];
if (bit==BIT_HIGH_SYMBOL) value+=Math.pow(2, i);
}
// stop bit: do nothing
// end bit: do nothing
debugInfo("MESSAGE =" + Integer.toBinaryString(value) + ":" + value);
*/
int correctedMessage = decodeUniqueMessageCorrected(nPeaks,index);
debugInfo("MESSAGE corrected=" + Integer.toBinaryString(correctedMessage) + ":" + correctedMessage);
return correctedMessage;
}
private int findStartBit(int[] bits, int startIndex){
// find carrier and start bit
int index = startIndex;
int highCounter = 0;
boolean startBitDetected = false;
do {
int bit = bits[index];
switch (bit) {
case BIT_HIGH_SYMBOL:
highCounter++; // carrier high bit
break;
case BIT_LOW_SYMBOL:
if (highCounter>CARRIER_MIN_HIGH_BITS) { // start-bit detected
startBitDetected = true;
}
else highCounter = 0; // reset carrier counter
break;
case BIT_NONE_SYMBOL:
highCounter = 0;// reset carrier counter
break;
}
index++;
if (index>=bits.length) return -1;
} while (!startBitDetected);
return index;
}
private int[] parseBits(int[] peaks){
// from the number of peaks array decode into an array of bits (2=bit-1, 1=bit-0, 0=no bit)
//
int i =0;
int lowCounter = 0;
int highCounter = 0;
int nBits = peaks.length /SLOTS_PER_BIT;
int[] bits = new int[nBits];
//i = findNextZero(peaks,i); // do not search for silence
i = findNextNonZero(peaks,i);
int nonZeroIndex = i;
if (i+ SLOTS_PER_BIT >= peaks.length) //non-zero not found
return bits;
do {
//int nPeaks = peaks[i]+peaks[i+1]+peaks[i+2]+peaks[i+3];
int nPeaks = 0;
for (int j = 0; j < SLOTS_PER_BIT; j++) {
nPeaks+= peaks[i+j];
}
int position = i/SLOTS_PER_BIT;
bits[position] = BIT_NONE_SYMBOL;
debugInfo("npeaks:i=" + i + ":pos=" + position+ ": nPeaks=" + nPeaks);
if (nPeaks>= LOW_BIT_N_PEAKS) {
//Log.w(TAG, "parseBits NPEAK=" + nPeaks);
bits[position] = BIT_LOW_SYMBOL;
lowCounter++;
}
if (nPeaks>=HIGH_BIT_N_PEAKS ) {
bits[position] = BIT_HIGH_SYMBOL;
highCounter++;
}
//if (nPeaks>5) bits[position] = 1;
//if (nPeaks>12) bits[position] = 2;
i=i+SLOTS_PER_BIT;
} while (SLOTS_PER_BIT+i<peaks.length);
lowCounter = lowCounter - highCounter;
debugInfo("parseBits nonZeroIndex=" + nonZeroIndex);
debugInfo("parseBits lows=" + lowCounter);
debugInfo("parseBits highs=" + highCounter);
return bits;
}
private int findNextNonZero(int[] peaks, int startIndex){
// returns the position of the next value != 0 starting form startIndex
int index = startIndex;
int value = 1;
do {
value = peaks[index];
index++;
} while (value==0 && index<peaks.length-1);
return index-1;
}
private int[] processSound(double[] sound){
// split the sound array into slots of N_POINTS and calculate the number of peaks
int nPoints = N_POINTS;
int nParts = sound.length / nPoints;
int[] nPeaks = new int[nParts];
int startIndex = 0;
int i = 0;
int peakCounter = 0;
do {
int endIndex = startIndex + nPoints;
int n = this.countPeaks(sound, startIndex, endIndex);
nPeaks[i] = n;
peakCounter += n;
i++;
startIndex = endIndex;
} while (i<nParts);
//} while (startIndex+nPoints<sound.length);
debugInfo("processSound() peaksCounter=" + peakCounter);
if (peakCounter < MINUMUM_NPEAKS) {
nPeaks = new int[0];
}
return nPeaks;
}
private int countPeaks(double[] sound, int startIndex, int endIndex){
// count the number of peaks in the selected interval
// peak identification criteria: sign changed and several significant samples (>PEAK_AMPLITUDE_TRESHOLD)
int index = startIndex;
int signChangeCounter = 0;
int numberSamplesGreaterThresdhold = 0;
int sign = 0; // initialized at the first significant value
do {
double value = sound[index];
if (Math.abs(value)>PEAK_AMPLITUDE_TRESHOLD)
numberSamplesGreaterThresdhold++; //significant value
// sign initialization: take the sign of the first significant value
if (sign==0 & numberSamplesGreaterThresdhold>0) sign = (int) (value / Math.abs(value));
boolean signChanged = false;
if (sign <0 & value >0) signChanged = true;
if (sign >0 & value <0) signChanged = true;
if (signChanged & numberSamplesGreaterThresdhold>NUMBER_SAMPLES_PEAK){
signChangeCounter++; // count peak
sign=-1*sign; //change sign
}
index++;
//debugInfo(">>>>>>>index=" + index + " sign=" + sign + " signChangeCounter=" + signChangeCounter + " value=" + value + " numberSamplesGreaterThresdhold=" + numberSamplesGreaterThresdhold);
} while (index<endIndex);
return signChangeCounter;
}
}
I have made a voice recorder app, and I want to show the duration of the recordings in a listview. I save the recordings like this:
MediaRecorder recorder = new MediaRecorder();
recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
folder = new File(Environment.getExternalStorageDirectory()
+ File.separator + "Audio recordings");
String[] files = folder.list();
int number = files.length + 1;
String filename = "AudioSample" + number + ".mp3";
File output = new File(Environment.getExternalStorageDirectory()
+ File.separator + "Audio recordings" + File.separator
+ filename);
FileOutputStream writer = new FileOutputStream(output);
FileDescriptor fd = writer.getFD();
recorder.setOutputFile(fd);
try {
recorder.prepare();
recorder.start();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
Log.e(LOG_TAG, "prepare() failed");
e.printStackTrace();
}
How can I get the duration in seconds of this file?
Thanks in advance
---EDIT
I got it working, I called MediaPlayer.getduration() inside the MediaPlayer.setOnPreparedListener() method so it returned 0.
MediaMetadataRetriever is a lightweight and efficient way to do this. MediaPlayer is too heavy and could arise performance issue in high performance environment like scrolling, paging, listing, etc.
Furthermore, Error (100,0) could happen on MediaPlayer since it's a heavy and sometimes restart needs to be done again and again.
Uri uri = Uri.parse(pathStr);
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(AppContext.getAppContext(),uri);
String durationStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
int millSecond = Integer.parseInt(durationStr);
The quickest way to do is via MediaMetadataRetriever. However, there is a catch
if you use URI and context to set data source you might encounter bug
https://code.google.com/p/android/issues/detail?id=35794
Solution is use absolute path of file to retrieve metadata of media file.
Below is the code snippet to do so
private static String getDuration(File file) {
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(file.getAbsolutePath());
String durationStr = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
return Utils.formateMilliSeccond(Long.parseLong(durationStr));
}
Now you can convert millisecond to human readable format using either of below formats
/**
* Function to convert milliseconds time to
* Timer Format
* Hours:Minutes:Seconds
*/
public static String formateMilliSeccond(long milliseconds) {
String finalTimerString = "";
String secondsString = "";
// Convert total duration into time
int hours = (int) (milliseconds / (1000 * 60 * 60));
int minutes = (int) (milliseconds % (1000 * 60 * 60)) / (1000 * 60);
int seconds = (int) ((milliseconds % (1000 * 60 * 60)) % (1000 * 60) / 1000);
// Add hours if there
if (hours > 0) {
finalTimerString = hours + ":";
}
// Prepending 0 to seconds if it is one digit
if (seconds < 10) {
secondsString = "0" + seconds;
} else {
secondsString = "" + seconds;
}
finalTimerString = finalTimerString + minutes + ":" + secondsString;
// return String.format("%02d Min, %02d Sec",
// TimeUnit.MILLISECONDS.toMinutes(milliseconds),
// TimeUnit.MILLISECONDS.toSeconds(milliseconds) -
// TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliseconds)));
// return timer string
return finalTimerString;
}
Either try this to get duration in milliseconds:
MediaPlayer mp = MediaPlayer.create(yourActivity, Uri.parse(pathofyourrecording));
int duration = mp.getDuration();
Or measure the time elapsed from recorder.start() till recorder.stop() in nanoseconds:
long startTime = System.nanoTime();
// ... do recording ...
long estimatedTime = System.nanoTime() - startTime;
Try use
long totalDuration = mediaPlayer.getDuration(); // to get total duration in milliseconds
long currentDuration = mediaPlayer.getCurrentPosition(); // to Gets the current playback position in milliseconds
Division on 1000 to convert to seconds.
Hope this helped you.
If the audio is from url, just wait for on prepared:
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
length = mp.getDuration();
}
});
Kotlin Extension Solution
You can add this to reliably and safely get your audio file's duration. If it doesn't exist or there is an error, you'll get back 0.
myAudioFile.getMediaDuration(context)
/**
* If file is a Video or Audio file, return the duration of the content in ms
*/
fun File.getMediaDuration(context: Context): Long {
if (!exists()) return 0
val retriever = MediaMetadataRetriever()
return try {
retriever.setDataSource(context, uri)
val duration = retriever.extractMetadata(METADATA_KEY_DURATION)
retriever.release()
duration.toLongOrNull() ?: 0
} catch (exception: Exception) {
0
}
}
If you are regularly working with String or Uri for files, I'd suggest also adding these useful helpers
fun Uri.asFile(): File = File(toString())
fun String?.asUri(): Uri? {
try {
return Uri.parse(this)
} catch (e: Exception) {
Sentry.captureException(e)
}
return null
}
fun String.asFile() = File(this)
According to Vijay's answer, The function gives us the duration of the audio/video file but unfortunately, there is an issue of a run time exception so I sorted out and below function work properly and return the exact duration of the audio or video file.
public String getAudioFileLength(String path, boolean stringFormat) {
StringBuilder stringBuilder = new StringBuilder();
try {
Uri uri = Uri.parse(path);
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(HomeActivity.this, uri);
String duration = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
int millSecond = Integer.parseInt(duration);
if (millSecond < 0) return String.valueOf(0); // if some error then we say duration is zero
if (!stringFormat) return String.valueOf(millSecond);
int hours, minutes, seconds = millSecond / 1000;
hours = (seconds / 3600);
minutes = (seconds / 60) % 60;
seconds = seconds % 60;
if (hours > 0 && hours < 10) stringBuilder.append("0").append(hours).append(":");
else if (hours > 0) stringBuilder.append(hours).append(":");
if (minutes < 10) stringBuilder.append("0").append(minutes).append(":");
else stringBuilder.append(minutes).append(":");
if (seconds < 10) stringBuilder.append("0").append(seconds);
else stringBuilder.append(seconds);
}catch (Exception e){
e.printStackTrace();
}
return stringBuilder.toString();
}
:)
You can use this readyMade method, hope this helps someone.
Example 1 : getAudioFileLength(address, true); // if you want in stringFormat
Example 2 : getAudioFileLength(address, false); // if you want in milliseconds
public String getAudioFileLength(String path, boolean stringFormat) {
Uri uri = Uri.parse(path);
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(Filter_Journals.this, uri);
String duration = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
int millSecond = Integer.parseInt(duration);
if (millSecond < 0) return String.valueOf(0); // if some error then we say duration is zero
if (!stringFormat) return String.valueOf(millSecond);
int hours, minutes, seconds = millSecond / 1000;
hours = (seconds / 3600);
minutes = (seconds / 60) % 60;
seconds = seconds % 60;
StringBuilder stringBuilder = new StringBuilder();
if (hours > 0 && hours < 10) stringBuilder.append("0").append(hours).append(":");
else if (hours > 0) stringBuilder.append(hours).append(":");
if (minutes < 10) stringBuilder.append("0").append(minutes).append(":");
else stringBuilder.append(minutes).append(":");
if (seconds < 10) stringBuilder.append("0").append(seconds);
else stringBuilder.append(seconds);
return stringBuilder.toString();
}
For me, the AudioGraph class came to the rescue:
public static async Task<double> AudioFileDuration(StorageFile file)
{
var result = await AudioGraph.CreateAsync(new AudioGraphSettings(Windows.Media.Render.AudioRenderCategory.Speech));
if (result.Status == AudioGraphCreationStatus.Success)
{
AudioGraph audioGraph = result.Graph;
var fileInputNodeResult = await audioGraph.CreateFileInputNodeAsync(file);
return fileInputNodeResult.FileInputNode.Duration.TotalSeconds;
}
return -1;
}
Kotlin shortest way to do it (if it is an audiofile):
private fun getDuration(absolutePath: String): String {
val retriever = MediaMetadataRetriever()
retriever.setDataSource(absolutePath)
//For format in string MM:SS
val rawDuration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
val duration = rawDuration.milliseconds
return format("%02d:%02d", duration.inWholeMinutes, duration.inWholeSeconds % 60)
}
private fun getDurationInSeconds(absolutePath: String): Long {
val retriever = MediaMetadataRetriever()
retriever.setDataSource(absolutePath)
//Return only value in seconds
val rawDuration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
return rawDuration.milliseconds.inWholeSeconds
}
After you write the file, open it up in a MediaPlayer, and call getDuration on it.
Have you looked at Ringdroid?. It's pretty light weight and the integration is straight forward. It works well with VBR media files as well.
For your problem with getting the duration, you might want to do something like below using Ringdroid.
public class AudioUtils
{
public static long getDuration(CheapSoundFile cheapSoundFile)
{
if( cheapSoundFile == null)
return -1;
int sampleRate = cheapSoundFile.getSampleRate();
int samplesPerFrame = cheapSoundFile.getSamplesPerFrame();
int frames = cheapSoundFile.getNumFrames();
cheapSoundFile = null;
return 1000 * ( frames * samplesPerFrame) / sampleRate;
}
public static long getDuration(String mediaPath)
{
if( mediaPath != null && mediaPath.length() > 0)
try
{
return getDuration(CheapSoundFile.create(mediaPath, null));
}catch (FileNotFoundException e){}
catch (IOException e){}
return -1;
}
}
Hope that helps
It's simply. use RandomAccessFile Below is the code snippet to do so
public static int getAudioInfo(File file) {
try {
byte header[] = new byte[12];
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
randomAccessFile.readFully(header, 0, 8);
randomAccessFile.close();
return (int) file.length() /1000;
} catch (Exception e) {
return 0;
}
}
You can, of course, be more complete depending on your needs
I believe the major problem comes from the read method in MicrophoneManager but cant see where the problem is. My console output for bytesread is 0 (this is in the other class AudioTransmitter). It seems to me like its not streaming audio data since the data sent off is none changing and like I said bytes read is 0.
public class MicrophoneManager{
// private TargetDataLine targetDataLine;
private float sampleRate = 8000.0F; //8000,11025,16000,22050,44100
private int sampleSizeInBits = 16; //8,16
private int channels = 1; //1,2
private boolean signed = true; //true,false
private boolean bigEndian = false; //true,false
private AudioFormat audioFormat;
// private AudioRecord audioRecord;
// private AudioInputStream ais;
private static MicrophoneManager singletonMicrophoneManager = null;
public AudioRecord audioRecord;
public int mSamplesRead; //how many samples read
public int buffersizebytes;
public int buflen;
public int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
public int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
public static byte[] buffer; //+-32767
public static final int SAMPPERSEC = 8000; //samp per sec 8000, 11025, 22050 44100 or 48000
public class MicrophoneManager{
// private TargetDataLine targetDataLine;
private float sampleRate = 8000.0F; //8000,11025,16000,22050,44100
private int sampleSizeInBits = 16; //8,16
private int channels = 1; //1,2
private boolean signed = true; //true,false
private boolean bigEndian = false; //true,false
private AudioFormat audioFormat;
// private AudioRecord audioRecord;
// private AudioInputStream ais;
private static MicrophoneManager singletonMicrophoneManager = null;
public AudioRecord audioRecord;
public int mSamplesRead; //how many samples read
public int buffersizebytes;
public int buflen;
public int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
public int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
public static short[] buffer; //+-32767
public static final int SAMPPERSEC = 8000; //samp per sec 8000, 11025, 22050 44100 or 48000
public MicrophoneManager() {
System.out.println("Initializing");
// audioFormat = new AudioFormat(sampleRate,sampleSizeInBits,channels,signed,bigEndian);
// audioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, (int) sampleRate, channels, AudioFormat.ENCODING_PCM_16BIT, buffersizebytes);
buffersizebytes = AudioRecord.getMinBufferSize(SAMPPERSEC,channelConfiguration,audioEncoding); //4096 on ion
buffer = new short[buffersizebytes];
buflen=buffersizebytes/2;
audioRecord = new AudioRecord(android.media.MediaRecorder.AudioSource.MIC,SAMPPERSEC,
channelConfiguration,audioEncoding,buffersizebytes); //constructor
}
public static MicrophoneManager getMicrophoneManager() throws Exception {
if (singletonMicrophoneManager == null) {
singletonMicrophoneManager = new MicrophoneManager();
singletonMicrophoneManager.initialize();
}
return singletonMicrophoneManager;
}
public void initialize() throws Exception {
}
public void startAudioInput(){
try {
audioRecord.startRecording();
mSamplesRead = audioRecord.read(buffer, 0, buffer.length);
audioRecord.stop();
} catch (Throwable t) {
// Log.e("AudioRecord", "Recording Failed");
System.out.println("Error Starting audio input"+t);
}
}
public void stopAudioInput(){
audioRecord.stop();
System.out.println("Stopping audio input");
}
public void finishAudioInput(){
audioRecord.release();
System.out.println("Finishing audio input");
}
public boolean available() throws Exception {
return true;
}
public int read(byte[] inBuf) throws Exception {
return audioRecord.read(inBuf,0,inBuf.length);
}
}
AudioTransmitter:
public class AudioTransmitter extends Thread{
private MicrophoneManager mm=null;
private boolean transmittingAudio = false;
private String host;
private int port;
private long id=0;
boolean run=true;
public AudioTransmitter(String host, int port, long id) {
this.host = host;
this.port = port;
this.id = id;
this.start();
}
public void run() {
System.out.println("creating audio transmitter host "+host+" port "+port+" id "+id);
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] chain, String authType) {
for (int j=0; j<chain.length; j++)
{
System.out.println("Client certificate information:");
System.out.println(" Subject DN: " + chain[j].getSubjectDN());
System.out.println(" Issuer DN: " + chain[j].getIssuerDN());
System.out.println(" Serial number: " + chain[j].getSerialNumber());
System.out.println("");
}
}
}
};
while (run) {
if(transmittingAudio) {
try {
if(mm==null) {
mm = new MicrophoneManager();
// mm.initialize();
}
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
SSLSocketFactory sslFact = sc.getSocketFactory();
SSLSocket socket = (SSLSocket)sslFact.createSocket(host, port);
socket.setSoTimeout(10000);
InputStream inputStream = socket.getInputStream();
DataInputStream in = new DataInputStream(new BufferedInputStream(inputStream));
OutputStream outputStream = socket.getOutputStream();
DataOutputStream os = new DataOutputStream(new BufferedOutputStream(outputStream));
PrintWriter socketPrinter = new PrintWriter(os);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
// socketPrinter.println("POST /transmitaudio?patient=1333369798370 HTTP/1.0");
socketPrinter.println("POST /transmitaudio?id="+id+" HTTP/1.0");
socketPrinter.println("Content-Type: audio/basic");
socketPrinter.println("Content-Length: 99999");
socketPrinter.println("Connection: Keep-Alive");
socketPrinter.println("Cache-Control: no-cache");
socketPrinter.println();
socketPrinter.flush();
// in.read();
mm.startAudioInput();
int buffersizebytes = AudioRecord.getMinBufferSize(8000, AudioFormat.CHANNEL_CONFIGURATION_MONO,AudioFormat.ENCODING_PCM_16BIT); //4096 on ion
System.out.println("audio started");
byte[] data = new byte[buffersizebytes];
while(transmittingAudio) {
// byte[] data = new byte[mm.available()];
int bytesRead = mm.read(data);
os.write(data,0,bytesRead);
os.flush();
// ca.transmitAxisAudioPacket(data);
// System.out.println("read "+data);
System.out.println("bytesRead "+bytesRead);
System.out.println("data "+Arrays.toString(data));
}
os.close();
mm.stopAudioInput();
} catch (Exception e) {
System.out.println("excpetion while transmitting audio connection will be closed"+e);
transmittingAudio=false;
}
}
else {
try {
Thread.sleep(1000);
} catch (Exception e){
System.out.println("exception while thread sleeping"+e);}
}
}
}
public void setTransmittingAudio(boolean transmittingAudio) {
this.transmittingAudio = transmittingAudio;
}
public void finished() {
this.transmittingAudio = false;
mm.finishAudioInput();
}
}
You are calling
mSamplesRead = audioRecord.read(buffer, 0, buffersizebytes);
There are a few problems with that.
audioRecord.read() wants the length of the array you are reading in to, not the size in bytes. You have set your encoding to 16 bit. You should really be doing something like:
new short[] buffer = short[1024]; // or whatever length you like
mSampleRead = audioRecord.read(buffer,0,buffer.length);
You are calling to read buffersizebytes but you set buffer = new byte[1024];. No particular reason to think buffersizebytes is the right number is there? You want a short[] array with 16 bit encoding, and you want the number of SAMPLES (not bytes) you read to be less than or equal to the length of that short[] buffer.
Also, you will be in better shape if you print out the exception you get when they are thrown, change
System.out.println("Error Starting audio input");
to
System.out.println("Error Starting audio input" + t);
and you will at least have a hint why android is throwing you in the dustbin.
Thank you guys for you help however I found a better way to do whats needed (here). Here is the code. Notice this also uses http://www.devdaily.com/java/jwarehouse/android/core/java/android/speech/srec/UlawEncoderInputStream.java.shtml to convert the audio to ulaw
public class AudioWorker extends Thread
{
private boolean stopped = false;
private String host;
private int port;
private long id=0;
boolean run=true;
AudioRecord recorder;
//ulaw encoder stuff
private final static String TAG = "UlawEncoderInputStream";
private final static int MAX_ULAW = 8192;
private final static int SCALE_BITS = 16;
private InputStream mIn;
private int mMax = 0;
private final byte[] mBuf = new byte[1024];
private int mBufCount = 0; // should be 0 or 1
private final byte[] mOneByte = new byte[1];
////
/**
* Give the thread high priority so that it's not canceled unexpectedly, and start it
*/
public AudioWorker(String host, int port, long id)
{
this.host = host;
this.port = port;
this.id = id;
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
// start();
}
#Override
public void run()
{
Log.i("AudioWorker", "Running AudioWorker Thread");
recorder = null;
AudioTrack track = null;
short[][] buffers = new short[256][160];
int ix = 0;
/*
* Initialize buffer to hold continuously recorded AudioWorker data, start recording, and start
* playback.
*/
try
{
int N = AudioRecord.getMinBufferSize(8000,AudioFormat.CHANNEL_IN_MONO,AudioFormat.ENCODING_PCM_16BIT);
recorder = new AudioRecord(AudioSource.MIC, 8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, N*10);
track = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,
AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, N*10, AudioTrack.MODE_STREAM);
recorder.startRecording();
track.play();
/*
* Loops until something outside of this thread stops it.
* Reads the data from the recorder and writes it to the AudioWorker track for playback.
*/
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
SSLSocketFactory sslFact = sc.getSocketFactory();
SSLSocket socket = (SSLSocket)sslFact.createSocket(host, port);
socket.setSoTimeout(10000);
InputStream inputStream = socket.getInputStream();
DataInputStream in = new DataInputStream(new BufferedInputStream(inputStream));
OutputStream outputStream = socket.getOutputStream();
DataOutputStream os = new DataOutputStream(new BufferedOutputStream(outputStream));
PrintWriter socketPrinter = new PrintWriter(os);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
// socketPrinter.println("POST /transmitaudio?patient=1333369798370 HTTP/1.0");
socketPrinter.println("POST /transmitaudio?id="+id+" HTTP/1.0");
socketPrinter.println("Content-Type: AudioWorker/basic");
socketPrinter.println("Content-Length: 99999");
socketPrinter.println("Connection: Keep-Alive");
socketPrinter.println("Cache-Control: no-cache");
socketPrinter.println();
socketPrinter.flush();
while(!stopped)
{
Log.i("Map", "Writing new data to buffer");
short[] buffer = buffers[ix++ % buffers.length];
N = recorder.read(buffer,0,buffer.length);
track.write(buffer, 0, buffer.length);
byte[] bytes2 = new byte[buffer.length * 2];
ByteBuffer.wrap(bytes2).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(buffer);
read(bytes2, 0, bytes2.length);
// os.write(bytes2,0,bytes2.length);
os.write(bytes2,0,bytes2.length);
System.out.println("bytesRead "+buffer.length);
System.out.println("data "+Arrays.toString(buffer));
}
os.close();
}
catch(Throwable x)
{
Log.w("AudioWorker", "Error reading voice AudioWorker", x);
}
/*
* Frees the thread's resources after the loop completes so that it can be run again
*/
finally
{
recorder.stop();
recorder.release();
track.stop();
track.release();
}
}
/**
* Called from outside of the thread in order to stop the recording/playback loop
*/
/**
* Called from outside of the thread in order to stop the recording/playback loop
*/
public void close()
{
stopped = true;
}
public void resumeThread()
{
stopped = false;
run();
}
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] chain, String authType) {
for (int j=0; j<chain.length; j++)
{
System.out.println("Client certificate information:");
System.out.println(" Subject DN: " + chain[j].getSubjectDN());
System.out.println(" Issuer DN: " + chain[j].getIssuerDN());
System.out.println(" Serial number: " + chain[j].getSerialNumber());
System.out.println("");
}
}
}
};
public static void encode(byte[] pcmBuf, int pcmOffset,
byte[] ulawBuf, int ulawOffset, int length, int max) {
// from 'ulaw' in wikipedia
// +8191 to +8159 0x80
// +8158 to +4063 in 16 intervals of 256 0x80 + interval number
// +4062 to +2015 in 16 intervals of 128 0x90 + interval number
// +2014 to +991 in 16 intervals of 64 0xA0 + interval number
// +990 to +479 in 16 intervals of 32 0xB0 + interval number
// +478 to +223 in 16 intervals of 16 0xC0 + interval number
// +222 to +95 in 16 intervals of 8 0xD0 + interval number
// +94 to +31 in 16 intervals of 4 0xE0 + interval number
// +30 to +1 in 15 intervals of 2 0xF0 + interval number
// 0 0xFF
// -1 0x7F
// -31 to -2 in 15 intervals of 2 0x70 + interval number
// -95 to -32 in 16 intervals of 4 0x60 + interval number
// -223 to -96 in 16 intervals of 8 0x50 + interval number
// -479 to -224 in 16 intervals of 16 0x40 + interval number
// -991 to -480 in 16 intervals of 32 0x30 + interval number
// -2015 to -992 in 16 intervals of 64 0x20 + interval number
// -4063 to -2016 in 16 intervals of 128 0x10 + interval number
// -8159 to -4064 in 16 intervals of 256 0x00 + interval number
// -8192 to -8160 0x00
// set scale factors
if (max <= 0) max = MAX_ULAW;
int coef = MAX_ULAW * (1 << SCALE_BITS) / max;
for (int i = 0; i < length; i++) {
int pcm = (0xff & pcmBuf[pcmOffset++]) + (pcmBuf[pcmOffset++] << 8);
pcm = (pcm * coef) >> SCALE_BITS;
int ulaw;
if (pcm >= 0) {
ulaw = pcm <= 0 ? 0xff :
pcm <= 30 ? 0xf0 + (( 30 - pcm) >> 1) :
pcm <= 94 ? 0xe0 + (( 94 - pcm) >> 2) :
pcm <= 222 ? 0xd0 + (( 222 - pcm) >> 3) :
pcm <= 478 ? 0xc0 + (( 478 - pcm) >> 4) :
pcm <= 990 ? 0xb0 + (( 990 - pcm) >> 5) :
pcm <= 2014 ? 0xa0 + ((2014 - pcm) >> 6) :
pcm <= 4062 ? 0x90 + ((4062 - pcm) >> 7) :
pcm <= 8158 ? 0x80 + ((8158 - pcm) >> 8) :
0x80;
} else {
ulaw = -1 <= pcm ? 0x7f :
-31 <= pcm ? 0x70 + ((pcm - -31) >> 1) :
-95 <= pcm ? 0x60 + ((pcm - -95) >> 2) :
-223 <= pcm ? 0x50 + ((pcm - -223) >> 3) :
-479 <= pcm ? 0x40 + ((pcm - -479) >> 4) :
-991 <= pcm ? 0x30 + ((pcm - -991) >> 5) :
-2015 <= pcm ? 0x20 + ((pcm - -2015) >> 6) :
-4063 <= pcm ? 0x10 + ((pcm - -4063) >> 7) :
-8159 <= pcm ? 0x00 + ((pcm - -8159) >> 8) :
0x00;
}
ulawBuf[ulawOffset++] = (byte)ulaw;
}
}
public static int maxAbsPcm(byte[] pcmBuf, int offset, int length) {
int max = 0;
for (int i = 0; i < length; i++) {
int pcm = (0xff & pcmBuf[offset++]) + (pcmBuf[offset++] << 8);
if (pcm < 0) pcm = -pcm;
if (pcm > max) max = pcm;
}
return max;
}
public int read(byte[] buf, int offset, int length) throws IOException {
if (recorder == null) throw new IllegalStateException("not open");
// return at least one byte, but try to fill 'length'
while (mBufCount < 2) {
int n = recorder.read(mBuf, mBufCount, Math.min(length * 2, mBuf.length - mBufCount));
if (n == -1) return -1;
mBufCount += n;
}
// compand data
int n = Math.min(mBufCount / 2, length);
encode(mBuf, 0, buf, offset, n, mMax);
// move data to bottom of mBuf
mBufCount -= n * 2;
for (int i = 0; i < mBufCount; i++) mBuf[i] = mBuf[i + n * 2];
return n;
}
}