How can I print an image on a Bluetooth printer in Android? - java
I have to print some data on thermal bluetooth printer, I'm doing with this:
String message="abcdef any message 12345";
byte[] send;
send = message.getBytes();
mService.write(send);
It works well for text, but not for images. I think I need to get the byte[] of the image data. I tried getting the data of the image this way:
Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.qrcode);
ByteArrayOutputStream stream=new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 90, stream);
byte[] image=stream.toByteArray();
Unfortunately the printer prints a lot of strange characters (approx. 50 cm of paper). I don't know how to print the image.
I would like to try getting the pixels of the bitmap and next converting it to a byte[] and sending it, but i don't know how to do it.
Thanks
UPDATE:
After so much time, i'm doing this: I have a method called print_image(String file), the which gets the path of the image that i want to print:
private void print_image(String file) {
File fl = new File(file);
if (fl.exists()) {
Bitmap bmp = BitmapFactory.decodeFile(file);
convertBitmap(bmp);
mService.write(PrinterCommands.SET_LINE_SPACING_24);
int offset = 0;
while (offset < bmp.getHeight()) {
mService.write(PrinterCommands.SELECT_BIT_IMAGE_MODE);
for (int x = 0; x < bmp.getWidth(); ++x) {
for (int k = 0; k < 3; ++k) {
byte slice = 0;
for (int b = 0; b < 8; ++b) {
int y = (((offset / 8) + k) * 8) + b;
int i = (y * bmp.getWidth()) + x;
boolean v = false;
if (i < dots.length()) {
v = dots.get(i);
}
slice |= (byte) ((v ? 1 : 0) << (7 - b));
}
mService.write(slice);
}
}
offset += 24;
mService.write(PrinterCommands.FEED_LINE);
mService.write(PrinterCommands.FEED_LINE);
mService.write(PrinterCommands.FEED_LINE);
mService.write(PrinterCommands.FEED_LINE);
mService.write(PrinterCommands.FEED_LINE);
mService.write(PrinterCommands.FEED_LINE);
}
mService.write(PrinterCommands.SET_LINE_SPACING_30);
} else {
Toast.makeText(this, "file doesn't exists", Toast.LENGTH_SHORT)
.show();
}
}
I did it based on this post
This is the class PrinterCommands:
public class PrinterCommands {
public static final byte[] INIT = {27, 64};
public static byte[] FEED_LINE = {10};
public static byte[] SELECT_FONT_A = {27, 33, 0};
public static byte[] SET_BAR_CODE_HEIGHT = {29, 104, 100};
public static byte[] PRINT_BAR_CODE_1 = {29, 107, 2};
public static byte[] SEND_NULL_BYTE = {0x00};
public static byte[] SELECT_PRINT_SHEET = {0x1B, 0x63, 0x30, 0x02};
public static byte[] FEED_PAPER_AND_CUT = {0x1D, 0x56, 66, 0x00};
public static byte[] SELECT_CYRILLIC_CHARACTER_CODE_TABLE = {0x1B, 0x74, 0x11};
public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, -128, 0};
public static byte[] SET_LINE_SPACING_24 = {0x1B, 0x33, 24};
public static byte[] SET_LINE_SPACING_30 = {0x1B, 0x33, 30};
public static byte[] TRANSMIT_DLE_PRINTER_STATUS = {0x10, 0x04, 0x01};
public static byte[] TRANSMIT_DLE_OFFLINE_PRINTER_STATUS = {0x10, 0x04, 0x02};
public static byte[] TRANSMIT_DLE_ERROR_STATUS = {0x10, 0x04, 0x03};
public static byte[] TRANSMIT_DLE_ROLL_PAPER_SENSOR_STATUS = {0x10, 0x04, 0x04};
}
As is seen in the print_image method I'm calling a method, called convertBitmap, and im sending a bitmap, this is the code:
public String convertBitmap(Bitmap inputBitmap) {
mWidth = inputBitmap.getWidth();
mHeight = inputBitmap.getHeight();
convertArgbToGrayscale(inputBitmap, mWidth, mHeight);
mStatus = "ok";
return mStatus;
}
private void convertArgbToGrayscale(Bitmap bmpOriginal, int width,
int height) {
int pixel;
int k = 0;
int B = 0, G = 0, R = 0;
dots = new BitSet();
try {
for (int x = 0; x < height; x++) {
for (int y = 0; y < width; y++) {
// get one pixel color
pixel = bmpOriginal.getPixel(y, x);
// retrieve color of all channels
R = Color.red(pixel);
G = Color.green(pixel);
B = Color.blue(pixel);
// take conversion up to one single value by calculating
// pixel intensity.
R = G = B = (int) (0.299 * R + 0.587 * G + 0.114 * B);
// set bit into bitset, by calculating the pixel's luma
if (R < 55) {
dots.set(k);//this is the bitset that i'm printing
}
k++;
}
}
} catch (Exception e) {
// TODO: handle exception
Log.e(TAG, e.toString());
}
}
This is the printer that i'm using, resolution: 8 dots/mm, 576 dots/line
And this is what I like to do (i did it with the same printer, but with an app downloaded from play store)
This is what i'm getting now
Closer:
Closer2:
A little part of the image can be seen, so I think that i'm closer to can print the image...
The image that i'm using is this (576x95):
And this is the converted image (i'm converting it with the upper code):
So, the answer is: what I'm doing wrong?, I think that the error is in this command:
public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, -128, 0};
But, how can I calculate the correct values for my image?, thanks
I solve it converting Bitmap to Byte array. Remember that your image must be black & white format.
For full source code:
https://github.com/imrankst1221/Thermal-Printer-in-Android
public void printPhoto() {
try {
Bitmap bmp = BitmapFactory.decodeResource(getResources(),
R.drawable.img);
if(bmp!=null){
byte[] command = Utils.decodeBitmap(bmp);
printText(command);
}else{
Log.e("Print Photo error", "the file isn't exists");
}
} catch (Exception e) {
e.printStackTrace();
Log.e("PrintTools", "the file isn't exists");
}
}
I also tried this and I got to my own solution and I think I figured out how the SELECT_BIT_IMAGE_MODE command works.
The command public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, 255, 3}; in the class PrinterCommands is the POS Command for image printing.
The first two are pretty standard, the next three determine the mode and the dimensions of the image to be printed. For the sake of this solution, let's just assume that the second element (33, we are indexed zero) is always 33.
The last two elements of that byte[] refers to the Width (in pixels) property of the image that you want to print, element 3 is sometimes referred to as nL and element 4 is sometimes referred to as nH. They are actually both referring to the Width, nL is the Low Byte while nH is the High Byte. This means that we can have at the most an image with a width of 1111 1111 1111 1111b (binary) which is 65535d (decimal), though I haven't tried it yet. If nL or nH aren't set to the proper values, then there will be trash characters printed along with the image.
Somehow, Android docs tells us that the limits of the value for a byte in a byte array is -128 and +127, when I tried to put in 255, Eclipse asked me to cast it to Byte.
Anyway, going back to nL and nW, for your case, you have an image with width 576, if we convert 576 to Binary, we get two bytes which would go like:
0000 0010 0100 0000
In this case, the Low Byte is 0100 0000 while the High Byte is 0000 0010. Convert it back to decimal and we get nL = 64 and nH = 2.
In my case, I printed an image that has width of 330px, converting 330 to binary we get:
0000 0001 0100 1010
In this case now, the Low Byte is 0100 1010 and the High Byte is 0000 0001. Converting to decimal, we get nL = 74 and nH = 1.
For more information, look at these documentation/tutorials:
Star Asia Mobile Printer Documentation
ECS-POS programming guide - really extensive
Another Documentation
The expanded version of the code above, with more explanation
Explanation of the code above
Hope these helps.
Solved!, I was doing a wrong printer initializing... The corect way is:
public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, 255, 3};
So, by this way the image is printed completely fine
EDIT: Update based on reading your question: https://stackoverflow.com/questions/16597789/print-bitmap-on-esc-pos-printer-java
I will assume that the printer you are printing to is the same as above, i.e. the Rego Thermal Printer. This, as you note, support the ESC/POS Page Description Language.
Printers interpret data streamed to them as either a marked up document (in a similar way to how browser interpret HTML). In some cases, the printer literally runs the document as a program (eg PostScript). Link: Page Description Languages.
Common languages are:
Yours: ESC/POS.
PostScript
PCL
ZPL
You need to read the specifications for your printer to determine which language to use - if you need to support any printer, then you have a very large job ahead of you :(
In ESC/POS, you will need to use the GS v 0 command (documented on p33). You do this by sending the the characters 0x1D7630 across the serial link, followed by a set of arguments:
ASCII: Gs v 0
Decimal: 29 118 48 m xL xH yL yH [d]k
Hexadecimal: 1D 76 30 m xL xH yL yH [d]k
Parameter definitions:
m:
0,48: normal mode (1:1 scale)
1,49: double-width
2,50: double-height
3,51: double-width + double-height
xL, xH specifies (xL + xH × 256) bytes in horizontal direction for the bit image.
yL, yH specifies (yL + yH × 256) dots in vertical direction for the bit image.
[d]k specifies the bit image data (raster format).
k indicates the number of bit image data. k is an explanation parameter; therefore, it does not need to be transmitted.
Notes:
When data [d]k is 1 specifies a bit printed to 1 and not printed to 0.
If a raster bit image exceeds one line of print area, the excess data is not printed.
This command executes paper feed for amount needed for printing the bit image regardless of the settings by ESC 2 or ESC 3.
After printing the bit image, this command sets the print position to the beginning of the line, and clears up the buffer.
When this command is executed, the data is transmitted and printed synchronously. So no other printing command is required.
There are several more extensive expositions:
http://nicholas.piasecki.name/blog/2009/12/sending-a-bit-image-to-an-epson-tm-t88iii-receipt-printer-using-c-and-escpos/
On SO in C#. While not Java, it is close enough to be a template.
Unfortunately there is no printer API in Android. If you feel strongly about this, do follow these issues:
https://code.google.com/p/android/issues/detail?id=40486
https://code.google.com/p/android/issues/detail?id=1148
I am new to ESC/POS and am struggling with it. I came across this page which seems to have some useful functions: http://code.taobao.org/p/printer/src/trunk/prtest/src/com/enjar/plugins/PrintTools_58mm.java
It's in Chinese though, but might be worth going through. If anybody figures it out, I would like to get enlightened too...
I know for evolute and AMDL bluetooth printers.First Read the protocol defination document of the printer that tells you what specific bytes you need for the device-
public void connect() throws Exception
{
BluetoothDevice printer = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(connParams);
Method m = printer.getClass().getMethod("createInsecureRfcommSocket",new Class[] { int.class });
sock = (BluetoothSocket)m.invoke(printer, Integer.valueOf(1));
sock.connect();
os=sock.getOutputStream();
in=sock.getInputStream();
}
After connecting through the above code you get the outputstream of the socket.Then convert your image to the corresponding byte through the tool provided with the printer you get something like
public byte[] Packet1={
(byte)0X8A,(byte)0XC6,(byte)0X94,(byte)0XF4,(byte)0X0B,(byte)0X5E,(byte)0X30,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X04,(byte)0X24,(byte)0X01,(byte)0X0C,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X01,(byte)0X08,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X04,(byte)0X24,(byte)0X05,(byte)0X0C,(byte)0X00,(byte)0X60,(byte)0X00,(byte)0X18,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X30,(byte)0X1E,(byte)0X10,(byte)0X60,(byte)0X00,(byte)0X18,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X70,(byte)0X3F,(byte)0X18,(byte)0XF0,(byte)0X00,(byte)0X3E,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X70,(byte)0X3C,(byte)0X39,(byte)0XF1,(byte)0X80,(byte)0X3E,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0XF8,(byte)0X7C,(byte)0X9F,(byte)0XF1,(byte)0X80,(byte)0X7F,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0XF9,(byte)0X9E,(byte)0X1C,(byte)0XFF,(byte)0XC2,(byte)0X7E,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0XF9,(byte)0X9E,(byte)0X1C,(byte)0XE7,(byte)0XE2,(byte)0X7E,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0XFB,(byte)0X1E,(byte)0X1C,(byte)0XFF,(byte)0XE7,(byte)0XBE,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X7B,(byte)0X16,(byte)0X1C,(byte)0XFF,(byte)0XDF,(byte)0X3E,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X71,(byte)0X12,(byte)0X1C,(byte)0XE7,(byte)0XF7,(byte)0X34,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X51,(byte)0X12,(byte)0X1C,(byte)0XF7,(byte)0XF7,(byte)0X24,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X49,(byte)0X12,(byte)0X1C,(byte)0XFF,(byte)0XF3,(byte)0X24,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X49,(byte)0X12,(byte)0X3F,(byte)0XFD,(byte)0XF3,(byte)0X24,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X49,(byte)0X96,(byte)0X3F,(byte)0XFC,(byte)0XF3,(byte)0X24,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X05,(byte)0X49,(byte)0X80,(byte)0X00,(byte)0X08,(byte)0X10,(byte)0X5E,(byte)0X28,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X30,(byte)0X25,(byte)
0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0XE0,(byte)0X74,(byte)0XA9,(byte)0X33,(byte)0X23,(byte)0X26,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)0X04
};
where 8A is starting byte C6 is mode byte (different for smart card , swipe and fingerprint) , 94 is font byte and last byte 04 is the end byte telling the hardware that this is end of the packet.Depending on size of the image you get several of these packets of length 256 byte (most printer).Write them to the outputStream.
os.write(Packet1)
This works for me:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inTargetDensity = 200;
options.inDensity = 200;
Bitmap bmp = BitmapFactory.decodeResource(context.getResources(), img, options);
Before using the BitmapFactory.Options, I was able to print only 60X60 image size, now i can print images of greater size as well.
use this code:
public static void print(Context context) {
String examplePath = "file:///sdcard/dcim/Camera/20111210_181524.jpg";
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType("image/jpeg");
sendIntent.putExtra(Intent.EXTRA_SUBJECT, "Photo");
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(examplePath));
sendIntent.putExtra(Intent.EXTRA_TEXT, "Enjoy the photo");
context.startActivity(Intent.createChooser(sendIntent, "Email:"));
}
Related
Print bitmap full page width using ESC/POS
I am currently implementing Android PrintService, that is able to print PDFs via thermal printers. I managed to convert PDF to bitmap using PDFRenderer and I am even able to print the document. The thing is, the document (bitmap) is not full page width. I am receiving the document in 297x420 resolution and I am using printer with 58mm paper. This is how I process the document (written in C#, using Xamarin): // Create PDF renderer var pdfRenderer = new PdfRenderer(fileDescriptor); // Open page PdfRenderer.Page page = pdfRenderer.OpenPage(index); // Create bitmap for page Bitmap bitmap = Bitmap.CreateBitmap(page.Width, page.Height, Bitmap.Config.Argb8888); // Now render page into bitmap page.Render(bitmap, null, null, PdfRenderMode.ForPrint); And then, converting the bitmap into ESC/POS: // Initialize result List<byte> result = new List<byte>(); // Init ESC/POS result.AddRange(new byte[] { 0x1B, 0x33, 0x21 }); // Init ESC/POS bmp commands (will be reapeated) byte[] escBmp = new byte[] { 0x1B, 0x2A, 0x01, (byte)(bitmap.Width % 256), (byte)(bitmap.Height / 256) }; // Iterate height for (int i = 0; i < (bitmap.Height / 24 + 1); i++) { // Add bitmapp commands to result result.AddRange(escBmp); // Init pixel color int pixelColor; // Iterate width for (int j = 0; j < bitmap.Width; j++) { // Init data byte[] data = new byte[] { 0x00, 0x00, 0x00 }; for (int k = 0; k < 24; k++) { if (((i * 24) + k) < bitmap.Height) { // Get pixel color pixelColor = bitmap.GetPixel(j, (i * 24) + k); // Check pixel color if (pixelColor != 0) { data[k / 8] += (byte)(128 >> (k % 8)); } } } // Add data to result result.AddRange(data); } // Add some... other stuff result.AddRange(new byte[] { 0x0D, 0x0A }); } // Return data return result.ToArray(); Current result looks like this: Thank you all in advance.
There is no magic "scale-to-page-width" command in the ESC/POS command-set, you need to know the max width of your printer, available in the manual, and then you can: Double the width and height for some image output commands -- You are using ESC *, which supports low-density, but height and width change in different ratios. Render the PDF wider to begin with - match the Bitmap size to the printer page width, and not the PDF page width. The same problem is solved at PDFrenderer setting scale to screen You can also simply stretch the image before you send it, if you are happy with the low quality. See: How to Resize a Bitmap in Android? Aside, your ESC * implementation is incorrect. There are two bytes for the width- Check the ESC/POS manual for the correct usage, or read over the correct implementations in PHP or Python that I've linked in another question: ESC POS command ESC* for printing bit image on printer
Converting byte array to png
I have a byte array obtained from an image using the following code. String path = "/home/mypc/Desktop/Steganography/image.png"; File file = new File(path); BufferedImage bfimage = ImageIO.read(file); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(bfimage, "png", baos); baos.flush(); byte[] img_in_bytes = baos.toByteArray(); baos.close(); Then I converted these bytes back to png image using the following code. BufferedImage final_img = ImageIO.read(new ByteArrayInputStream(img_in_bytes)); File output_file = new File("Stegano2.png"); ImageIO.write(final_img, "png", output_file); It is perfectly fine if i just execute this piece of code. But if i try to modify some of the bytes in between, say like this : Insert_number_to_image(image_in_bytes, 10); and my method "Inset_number_to_image" goes like this : static void Insert_number_to_image(byte[] image, int size){ byte[] size_in_byte = new byte[4]; size_in_byte[0] = (byte)(size >>> 0); size_in_byte[1] = (byte)(size >>> 8); size_in_byte[2] = (byte)(size >>> 16); size_in_byte[3] = (byte)(size >>> 24); byte temp; int count = 0; for(int i=0; i<4; i++) { for(int j=0; j<8; j++) { temp = size_in_byte[i]; temp = (byte)(temp >>> j); temp = (byte)(temp & 1); if(temp == 1) { image[count] = (byte)(image[count] | 1); } else if(temp == 0) { image[count] = (byte)(image[count] & (byte)(~(1))); } count++; } } } then after that, when i save the modified byte array as png image using same code mentioned above, i am getting this error : Exception in thread "main" java.lang.IllegalArgumentException: image == null! at javax.imageio.ImageTypeSpecifier.createFromRenderedImage(ImageTypeSpecifier.java:925) at javax.imageio.ImageIO.getWriter(ImageIO.java:1591) at javax.imageio.ImageIO.write(ImageIO.java:1520) at Steganography.main(Steganography.java:211)
What you're using is the raw bytestream of a PNG image. PNG is a compressed format, where the bytestream doesn't reflect any of the pixel values directly and even changing one byte might irreversibly corrupt the file. What you want instead is to extract the pixel data to a byte array with byte[] pixels = ((DataBufferByte) img.getRaster().getDataBuffer()).getData(); Now you can modify the pixel values however you want. When you are ready to save that back to a file, convert the pixel byte array to a BufferedImage by putting your pixel array in a DataBufferByte object and passing that to a WriteableRaster, which you then use to create a BufferedImage. Your method would work for formats where the raw bytestream does directly represent the pixels, such as in BMP. However, even then you'd have to skip the first few bytes to avoid corrupting the header.
AudioRecorder quality degradation when change from byte to short and back to byte
I am writing a recording app for android using AudioRecorder Class. The code records audio raw data in a byte array then saves it to a WAV file till now all is well. Here is my problem: I need to change each 2 bytes to one short so i can work on audio data then change it back to bytes so I can write to output stream file. For now there is NO work on audio data just moving from byte to short and back again but for some reason the there is a bad degradation in audio quality here is the code for moving from byte to short and back again: //16 bit per sample to get correct value of sample convert to short private short[] ByteToShort (byte[] test) { short[] Sh = new short[Alldata.size()/2]; for(int ii=0;ii<Alldata.size()/2;ii++) { //Sh[ii] = (short) ((short)Alldata.get(2*ii) | ((short)Alldata.get(2*ii+1))<<8); Sh[ii] = (short) ((short)test[2*ii] | ((short)test[2*ii+1])<<8); } return Sh; } //change back to bytes for input/output streamfiles byte[] ShortToByte(short[] data) { byte[] dataByte = new byte[(int) (data.length*2)]; for(int ii=0;ii<data.length;ii++) { dataByte[2*ii] = (byte)(data[ii] & 0x00ff); dataByte[2*ii+1] = (byte)((data[ii] & 0xff00) >> 8); } return dataByte; }
You have to take into account that byte is signed. Sh[ii] = (short) (test[2 * ii] & 0xFF | (test[2 * ii + 1] & 0xFF) << 8);
Java: retrieving byte array from an 8 bit wav file and normalizing it to -1.0 to 1.0
Bear with me as Im very new with working with audio and I have been googling for days for a solution and not finding any. So i retrieve the byte array of a .wav file with this (source: Wav file convert to byte array in java) ByteArrayOutputStream out = new ByteArrayOutputStream(); BufferedInputStream in = new BufferedInputStream(new FileInputStream(WAV_FILE)); int read; byte[] buff = new byte[1024]; while ((read = in.read(buff)) > 0) { out.write(buff, 0, read); } out.flush(); byte[] audioBytes = out.toByteArray(); And then i convert the byte array to a float array and normalize it from -1.0 to 1.0. (source: Convert wav audio format byte array to floating point) ShortBuffer sbuf = ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer(); short[] audioShorts = new short[sbuf.capacity()]; sbuf.get(audioShorts); float[] audioFloats = new float[audioShorts.length]; for (int i = 0; i < audioShorts.length; i++) { audioFloats[i] = ((float)audioShorts[i])/0x8000; } return audioFloats; Later i convert this to line drawings which outputs the waveform using java.swing class Panel2 extends JPanel { float[] audioFloats; Dimension d; public Panel2(Dimension d, float[] audioFloats) { // set a preferred size for the custom panel. this.d = d; setPreferredSize(d); this.audioFloats = audioFloats; } #Override public void paint(Graphics g) { //super.paintComponent(g); super.paint(g); //shift by 45 because first 44 bytes used for header for (int i = 45; i<audioFloats.length; i++){ Graphics2D g2 = (Graphics2D) g; float inc = (i-45)*((float)d.width)/((float)(audioFloats.length-45-1)); Line2D lin = new Line2D.Float(inc, d.height/2, inc, (audioFloats[i]*d.height+d.height/2)); g2.draw(lin); } } } The waveform only looks right for 16 bit wav files (ive cross checked with goldwave and both my waveform and their waveform look similar for 16 bits). How do i do this for 8 bit .wav files? Because this is for homework, my only restriction is read the wav file byte by byte. I also know the wav files are PCM coded and have the first 44 bytes reserved as the header
You need to adapt this part of the code: ShortBuffer sbuf = ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer(); short[] audioShorts = new short[sbuf.capacity()]; sbuf.get(audioShorts); float[] audioFloats = new float[audioShorts.length]; for (int i = 0; i < audioShorts.length; i++) { audioFloats[i] = ((float)audioShorts[i])/0x8000; } You don't need ByteBuffer at all—you already have your byte array. So just convert it to floats: float[] audioFloats = new float[audioBytes.length]; for (int i = 0; i < audioBytes.length; i++) { audioFloats[i] = ((float)audioBytes[i])/0x80; }
Audio streams are usually interleaved with one channel of data then the opposite channel of data. So for example the first 16 bits would be the left channel, then the next 16 bits would be the right channel. Each of these is considered 1 frame of data. I would make sure that your 8 bit stream is only one channel because it looks like the methods are only set up to read one channel. Also in your example to convert the frames you are grabbing the individual channel as a short then finding a decimal by dividing that by 0x8000 hex or the maximum value of a signed short. short[] audioShorts = new short[sbuf.capacity()]; sbuf.get(audioShorts); ... audioFloats[i] = ((float)audioShorts[i])/0x8000; My guess is that you need to read the 8 byte stream as a type 'byte' instead of a short then divide that by 128 or the maximum value of a signed 8 bit value. This will involve making a whole new method that processes 8 bit streams instead of 16 bit streams. With the following changes. byte[] audioBytes = new byte[sbuf.capacity()]; sbuf.get(audioBytes); ... audioFloats[i] = ((float)audioBytes[i])/0x80;
Java beginner: convert an image to a binary array
this is what I've gone so far and I can't seem to go further because I don't understand bitwise operations on the RGB // Read from a file File file = new File("src/a.gif"); image = ImageO.read(file); int[] rgbarr = image.getRGB(0, 0, 13, 15, null, 0, 13);// image is 13*15 System.out.println(rgbarr.length); for (int i : rgbarr) { System.out.println(i); } Output: was values such as -16777216 and -1 Because I've already made an image black and white just to ease my understanding But in our case here let's suppose it would be just random image , how do I get from normal RGB image to binary values (0 or 1) for each pixel.
I'm betting that each int in the array is a packed ARGB value; boolean [] output = new boolean[rgbarr.length]; for ( int i=0; i<rgbarr.length; ++i ) { int a = (rgbarr[i] >> 24 ) & 0x0FF; int r = (rgbarr[i] >> 16 ) & 0x0FF; int g = (rgbarr[i] >> 8 ) & 0x0FF; int b = (rgbarr[i] ) & 0x0FF; output[i] = (0.30*r + 0.59*g + 0.11*b) > 127; } The output equation I choose was taken from a definition of Luminance from wikipedia. You can change the scales so long as the coefficients add up to 1.
You can do some thing like this, BufferedImage img = (BufferedImage) image; ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(img , "jpg", baos); byte[] previewByte = baos.toByteArray(); Hope this helps!!
You already have RGB values in the int[], that is color of each pixel. You could compare this to a specific background color, like black, to get a 1-bit pixel value. Then maybe set a bit in another appropriately sized byte[] array...