I sort of understand what it's doing, but what is the logic behind the steps in the code provided below? It's a way of loading a texture in LWJGL. But what is happening in the for loop? Wouldn't you just multiply x and y to get the location of a pixel? Any explanation of whats going on from the for loop to the end of the code would be helpful, as the comments are vary vague when it gets to the for loop. I don't understand the weird symbols when putting pixel info into the buffers.
public class TextureLoader {
private static final int BYTES_PER_PIXEL = 4;//3 for RGB, 4 for RGBA
public static int loadTexture(BufferedImage image){
int[] pixels = new int[image.getWidth() * image.getHeight()];
image.getRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());
ByteBuffer buffer = BufferUtils.createByteBuffer(image.getWidth() * image.getHeight() * BYTES_PER_PIXEL); //4 for RGBA, 3 for RGB
for(int y = 0; y < image.getHeight(); y++){
for(int x = 0; x < image.getWidth(); x++){
int pixel = pixels[y * image.getWidth() + x];
buffer.put((byte) ((pixel >> 16) & 0xFF)); // Red component
buffer.put((byte) ((pixel >> 8) & 0xFF)); // Green component
buffer.put((byte) (pixel & 0xFF)); // Blue component
buffer.put((byte) ((pixel >> 24) & 0xFF)); // Alpha component. Only for RGBA
}
}
buffer.flip(); //FOR THE LOVE OF GOD DO NOT FORGET THIS
// You now have a ByteBuffer filled with the color data of each pixel.
// Now just create a texture ID and bind it. Then you can load it using
// whatever OpenGL method you want, for example:
int textureID = glGenTextures(); //Generate texture ID
glBindTexture(GL_TEXTURE_2D, textureID); //Bind texture ID
//Setup wrap mode
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
//Setup texture scaling filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//Send texel data to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, image.getWidth(), image.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
//Return the texture ID so we can bind it later again
return textureID;
}
public static BufferedImage loadImage(String loc)
{
try {
return ImageIO.read(DefenseStep.class.getResource(loc));
} catch (IOException e) {
//Error Handling Here
}
return null;
}
}
All it's doing is loading the colors from the image into a buffer pixel-by-pixel.
This code is doing it using the bitwise operators from Java. See this Java trail.
When you see >>, this means "shift the binary of this number to the right," and when you see num >> n, this means "shift the binary of num's value n bits to the right. For example:
System.out.println(4 >> 2); // Prints "1"
This prints 1 because 4 in binary is 0100, and when shifted right by 2 bits, you get 0001, which is 1 in decimal.
Now, that being said, colors in an image are represented using ARGB. This means that every 32 bits from the image has 8 bits dedicated to each of A, R, G, and B (alpha, red, green, and blue), so its hexadecimal form looks like this:
0xAARRGGBB
Where each letter is a hexadecimal digit.
The code you've posted is using binary logic to retrieve each group of AA, RR, etc. Each group is exactly one byte, or 8 bits, so that's where the 8, 16, and 24 come from. & does a bitwise logical AND on the two numbers, where only bit positions that are 1 in both numbers remain 1, and every other position becomes 0.
For a concrete example, let's retrieve the RR byte from purple in ARGB.
In ARGB, yellow is A=255, R=127, G=0, and B=127, so our hexadecimal version of this is:
0xAARRGGBB
0xFF7F007F
Looking at the hexadecimal value, we see that RR is the third byte from the end. To get RR when the ARGB value is in a variable, let's start by putting this into int pixel:
int pixel = 0xFF7F007F;
Note the similarity to your code. Each int in the pixel matrix is an ARGB color.
Next, we'll shift the number right by 2 bytes so RR is the lowest byte, which gives us:
0x0000AARR
0x0000FF7F
This is done with this code:
int intermediate = pixel >> 16;
The 16 here comes from the fact that we want to shift right by 2 bytes, and each byte contains 8 bits. The >> operator expects bits, so we have to give it 16 instead of 2.
Next, we want to get rid of AA, but keep RR. To do this, we use what's called a bitmask along with the & operator. Bitmasks are used to single out the particular bits of a binary number. Here, we want 0xFF. This is exactly eight 1's in binary. (Each F in hexadecimal is 1111 in binary.)
Bear with me, cause this will look ugly. When we do int red = intermediate & 0xFF, what it's doing (in binary) is:
0000 0000 0000 0000 1111 1111 0111 1111 (0x00007F7F)
& 0000 0000 0000 0000 0000 0000 1111 1111 (0x000000FF)
= 0000 0000 0000 0000 0000 0000 0111 1111 (0x0000007F)
Remember that & means that the resulting bit is only 1 if both input bits are 1.
So we get that red = 0x7F or red = 127, which is exactly what we had above.
Edit:
Why is he looping through the image's pixels starting from y, then x, instead of x then y? And when he creates the variable pixel, why would he multiply y by width and add x? Shouldn't it just be x * y to get the pixel?
Let's use a simple 3x3 image to demonstrate. In a 3x3 image, you have 9 pixels, which means the pixels array has 9 elements. These elements are created by getRGB in a row-by-row order with respect to the image, so the pixel/index relationship looks like this:
0 1 2
3 4 5
6 7 8
The positions correspond to the index used to get that pixel. So to get the top-left pixel of the image, (0, 0), I use pixel[0]. To get the center pixel, (1, 1), I use pixel[4]. To get the pixel under the center pixel, (1, 2), I use pixel[7].
Notice that this produces a 1:1 mapping for image coordinate to index, like so:
Coord. -> Index
---------------
(0, 0) -> 0
(1, 0) -> 1
(2, 0) -> 2
(0, 1) -> 3
(1, 1) -> 4
(2, 1) -> 5
(0, 2) -> 6
(1, 2) -> 7
(2, 2) -> 8
The coordinates are (x, y) pairs, so we need to figure out a mathematical way to turn x and y pairs into an index.
I could get into some fun math, but I won't do that for sake of simplicity. Let's just start with your proposal, using x * y to get the index. If we do that, we get:
Coord. -> Index
-------------------
(0, 0) -> 0 * 0 = 0
(1, 0) -> 1 * 0 = 0
(2, 0) -> 2 * 0 = 0
(0, 1) -> 0 * 1 = 0
(1, 1) -> 1 * 1 = 1
(2, 1) -> 2 * 1 = 2
(0, 2) -> 0 * 2 = 0
(1, 2) -> 1 * 2 = 2
(2, 2) -> 2 * 2 = 4
This isn't the mapping we have above, so using x * y won't work. Since we can't change how getRGB orders the pixels, we need it to match the mapping above.
Let's try his solution. His equation is x = y * w, where w is the width, in this case, 3:
Coord. -> Index
-----------------------
(0, 0) -> 0 + 0 * 3 = 0
(1, 0) -> 1 + 0 * 3 = 1
(2, 0) -> 2 + 0 * 3 = 2
(0, 1) -> 0 + 1 * 3 = 3
(1, 1) -> 1 + 1 * 3 = 4
(2, 1) -> 2 + 1 * 3 = 5
(0, 2) -> 0 + 2 * 3 = 6
(1, 2) -> 1 + 2 * 3 = 7
(2, 2) -> 2 + 2 * 3 = 8
See how the mappings line up to those above? This is what we wanted. Basically what y * w is doing here is skipping the first y * w pixels in the array, which is exactly the same as skipping y rows of pixels. Then by iterating through x, we're iterating through each pixel of the current row.
In case it's not clear from the explanation above, we iterate over y then x because the pixels are added row-by-row to the array in horizontal (x) order, so the inner loop should iterate over the x value so that we aren't jumping around. If we used the same y * w + x, then iterating over x then y would cause the iteration to go 0, 3, 6, 1, 4, 7, 2, 5, 8, which is undesirable since we need to add the colors to the byte buffer in the same order as the pixel array.
Each pixel is represented by a 32-bit integer. The leftmost eight bits of that integer are its alpha component, followed by red, followed by green, followed by blue.
(pixel >> 16) & 0xFF shifts the integer sixteen bits to the right, so the rightmost eight bits in it are now the red component. It then uses a bit mask to set all other bits to zero, so you're left with just the red component. The same logic applies for the other components.
Further reading.
Weird symbols you are referring to are shift operators, and bitwise AND operators I think.
>> n shifts right with n bits
&& 0xFF means that you take the lowest 8 bits of a given binary value
So in short: The for loop decomposes the pixel variable into 4 different 8bit parts: the highest 8 bit will be the alpha, the second the red, the third the green, and the last the blue
So this is the map of the 32bits:
AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB
Where:
A: alpha
R: red
G: green
B: blue
well each component of RGBA (red, green, blue, alpha) has 256 = 2^8 (= 1 byte) different values. Concatenating each component yields a 32 bit binary string which the for loop is loading into buffer byte-wise.
Related
I have bitmap and triangles some of which change vertices in real time, each triangle contains a value of the average color inside that same triangle. But generating the average color in a triangle is costly operation, that is why I need to generate average color only for those triangle that change at least one vertex, and use the previously generated colors for all triangles that do not change vertices.
If we have the triangles ▲ACD, ▲ABC and ▲BEC and I found there corresponding colors: green, blue, red. Then vertex E changes position and new triangle pop-up ▲CED, so I need to generate its average color, but since the existing triangle ▲BEC is also change it need to generate its average color too. That mean triangles ▲ACD and ▲ABC can use the previously generated color since there vertex coordinates are not changed.
What is the fastest way to get the previously generated colors for triangles that does not change vertices. Triangles are generated with Delaunay Triangulation, so triangles order changes, that means first we can have an array [ACD, ABC, BEC] and after vertex E changes position we can have array [CED, BEC, ABC, ACD].
If I have 2000 triangles and 3 triangles change there vertices, so the rest 1997 triangles can use there previously generated colors. I have the vertices coordinates for each triangle and also there corresponding colors. If I check each of those 1997 triangle with each of the previous 2000 triangle(triangles that contains the previously generated colors) to determine if triangles match and get the corresponding color that is ~1997*2000 = ~3,994,000 total if checks in order to determine the previously generated colors for each triangle.
That is big amount of operations that need to be done in real time. Is there any way I can speed thing up, using something like HashMaps, where I can use the pair coordinates as key, and color as value or some other way?
To generate the Delaunay Triangulation from points I use this library. It keeps the points in the array points, and the triangle indices in the array triangles. That means to get the triangle coordinates I create extension fucntion with the following code:
fun Delaunay.getTriangleCoordinates(): FloatArray {
val coordinates = FloatArray(triangles.size * 2)
for (i in 0 until triangles.size / 3) {
// get triangle points(vertices) indices
val t0 = triangles[i * 3]
val t1 = triangles[i * 3 + 1]
val t2 = triangles[i * 3 + 2]
// vertex 1
coordinates[i * 6 + 0] = points[t0].x
coordinates[i * 6 + 1] = points[t0].y
// vertex 2
coordinates[i * 6 + 2] = points[t1].x
coordinates[i * 6 + 3] = points[t1].y
// vertex 3
coordinates[i * 6 + 4] = points[t2].x
coordinates[i * 6 + 5] = points[t2].y
}
return coordinates
}
In order to check only one value and not all vertices of a particular triangle, we can get the center of each triangle and use it as key point. It is demonstrated in the code below, again using extension function:
fun Delaunay.getTriangleCenterCoordinates(): FloatArray {
val coordinates = FloatArray((triangles.size / 3) * 2)
for (i in 0 until triangles.size / 3) {
val t0 = triangles[i * 3]
val t1 = triangles[i * 3 + 1]
val t2 = triangles[i * 3 + 2]
coordinates[i * 2] = (points[t0].x + points[t1].x + points[t2].x) / 3
coordinates[i * 2 + 1] = (points[t0].y + points[t1].y + points[t2].y) / 3
}
return coordinates
}
In order to draw the triangle with OpenGL I need all triangle coordinates as FloatArray, just what we get with getTriangleCoordinates(). I also need the corresponding color for each triangle as IntArray().
Here is the code that gets the average color for triangle with changed vertices or use the previously generated color for triangles with unchanged vertices.
var previousTriangleColors = IntArray(0)
var previousTriangleCenterCoordinates = FloatArray(0)
fun renderDelaunay() {
// at least three points acquired
if (delaunayPoints.size >= 3) {
delaunay = Delaunay(delaunayPoints)
val triangleCoordinate = delaunay.getTriangleCoordinates()
val triangleCenterCoordinate = delaunay.getTriangleCenterCoordinates()
val colors = IntArray(triangleCenterCoordinate.size / 2)
for (i in 0 until triangleCenterCoordinate.size / 2) {
// check if triangle vertices are changed or not
var foundIndex = -1
for (j in 0 until previousTriangleCenterCoordinates.size / 2) {
if (triangleCenterCoordinate[i * 2] == previousTriangleCenterCoordinates[j * 2] &&
triangleCenterCoordinate[i * 2 + 1] == previousTriangleCenterCoordinates[j * 2 + 1]
) {
foundIndex = j
break
}
}
if (foundIndex != -1) {
// if triangle center is NOT changed, then use previously generated color
colors[i] = previousTriangleColors[foundIndex]
} else {
// if triangle center is changed, then generate new average color
// first triangle vertex
val x1 = getImageGraphicCoordinateX(triangleCoordinate[i * 6])
val y1 = getImageGraphicCoordinateY(triangleCoordinate[i * 6 + 1])
// second triangle vertex
val x2 = getImageGraphicCoordinateX(triangleCoordinate[i * 6 + 2])
val y2 = getImageGraphicCoordinateY(triangleCoordinate[i * 6 + 3])
// third triangle vertex
val x3 = getImageGraphicCoordinateX(triangleCoordinate[i * 6 + 4])
val y3 = getImageGraphicCoordinateY(triangleCoordinate[i * 6 + 5])
// generate new average color for the triangle
colors[i] = averageColor(x1.toFloat(), y1.toFloat(), x2.toFloat(), y2.toFloat(), x3.toFloat(), y3.toFloat())
}
}
previousTriangleColors = colors
previousTriangleCenterCoordinates = triangleCenterCoordinate
}
}
Those two nested for loops works well on emulator, but are slow on actual android device, since it use costly operations. For example if the bitmap is with highly resolution 4096x4096, that means each triangle will have big area that will be used to generate the average color. And if I have 5000 triangles, with the two nested for loop we get ~5000x5000= 25,000,000 if checks, to see if triangle vertices coordinates are changed or not. I can reduce the bitmap size, and reduce the area that is used to generate the average color, but that is not the point. Since more than 98% of the triangles are not changed when single vertex is changed in real time, we can use the previously generated colors, but those two nested loops are the reason the app experience lagging. So I need a better way to check if triangle coordinates are changed.
First of all, thanks for reading.
I am writing a class which returns an image from 2D double array(kDEvalue from the code).
However, since so many values(256*256) vary in a very small range(-1 to 1), i cannot use the default color scaling(it is all black if i use the value from the array).
BufferedImage image = new BufferedImage(kDEvalues.length, kDEvalues[0].length, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < 256; x++) {
for (int y = 0; y < 256; y++) {
image.setRGB(x, y, (255*(int)Math.round(kDEvalues[x][y]))/100);
}
}
try {
ImageIO.write(image, "jpg", new File("CustomImage.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
In this case, how I rescale the color in grey scale so that I can see the varience of the value? Thanks in advance
You almost got it right here:
image.setRGB(x, y, (255*(int)Math.round(kDEvalues[x][y]))/100);
There are three things wrong with your expression for the RGB value. First, you round and then multiply. This drops any fractions of your small values, it should be the other way around. Second you only create a value for one color channel (the blue one) and thrid, you do not check for overflow.
Try something like this:
int gray = (int) Math.round((kDEvalues[x][y] - (-1)) * 127.5);
// limit range to 0-255
gray = Math.min(gray, 255);
gray = Math.max(gray, 0);
// copy gray value to r, g and b channel
int rgb = (gray << 16) | (gray << 8) | gray;
image.setRGB(x, y, rgb);
First operation is to scale the value to 0-255. Since you say your values are in range -1 to 1, adjust the values so the lower bound falls on zero (by subtracting the lower bound). Then multiply by a factor that the highes value becomes 255 (thats (1 - (-1)) * 127.5 = 255).
Just to be safe, clip the resulting value to 0-255 (Math.min/max expressions).
Then replicate the gray value in RGB by setting the gray value to all 3 color channels. The format is documented in the javadoc and the bit shifts just place the gray value at the correct position within the int.
This answer suggests that it's over 10 times faster to loop pixel array instead of using BufferedImage.getRGB. Such difference is too important to by ignored in my computer vision program. For that reason, O rewritten my IntegralImage method to calculate integral image using the pixel array:
/* Generate an integral image. Every pixel on such image contains sum of colors or all the
pixels before and itself.
*/
public static double[][][] integralImage(BufferedImage image) {
//Cache width and height in variables
int w = image.getWidth();
int h = image.getHeight();
//Create the 2D array as large as the image is
//Notice that I use [Y, X] coordinates to comply with the formula
double integral_image[][][] = new double[h][w][3];
//Variables for the image pixel array looping
final int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
//final byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
//If the image has alpha, there will be 4 elements per pixel
final boolean hasAlpha = image.getAlphaRaster() != null;
final int pixel_size = hasAlpha?4:3;
//If there's alpha it's the first of 4 values, so we skip it
final int pixel_offset = hasAlpha?1:0;
//Coordinates, will be iterated too
//It's faster than calculating them using % and multiplication
int x=0;
int y=0;
int pixel = 0;
//Tmp storage for color
int[] color = new int[3];
//Loop through pixel array
for(int i=0, l=pixels.length; i<l; i+=pixel_size) {
//Prepare all the colors in advance
color[2] = ((int) pixels[pixel + pixel_offset] & 0xff); // blue;
color[1] = ((int) pixels[pixel + pixel_offset + 1] & 0xff); // green;
color[0] = ((int) pixels[pixel + pixel_offset + 2] & 0xff); // red;
//For every color, calculate the integrals
for(int j=0; j<3; j++) {
//Calculate the integral image field
double A = (x > 0 && y > 0) ? integral_image[y-1][x-1][j] : 0;
double B = (x > 0) ? integral_image[y][x-1][j] : 0;
double C = (y > 0) ? integral_image[y-1][x][j] : 0;
integral_image[y][x][j] = - A + B + C + color[j];
}
//Iterate coordinates
x++;
if(x>=w) {
x=0;
y++;
}
}
//Return the array
return integral_image;
}
The problem is that if I use this debug output in the for loop:
if(x==0) {
System.out.println("rgb["+pixels[pixel+pixel_offset+2]+", "+pixels[pixel+pixel_offset+1]+", "+pixels[pixel+pixel_offset]+"]");
System.out.println("rgb["+color[0]+", "+color[1]+", "+color[2]+"]");
}
This is what I get:
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
...
So how should I properly retrieve pixel array for BufferedImage images?
A bug in the code above, that is easily missed, is that the for loop doesn't loop as you'd expect. The for loop updates i, while the loop body uses pixel for its array indexing. Thus, you will only ever see the values of pixel 1, 2 and 3.
Apart from that:
The "problem" with the negative pixel values, is most likely that the code assumes a BufferedImage that stores its pixels in "pixel interleaved" form, however, they are stored "pixel packed". That is, all samples (R, G, B and A) for one pixel is stored in a single sample, an int. This will be the case for all BufferedImage.TYPE_INT_* types (while the BufferedImage.TYPE_nBYTE_* types are stored interleaved).
It's completely normal to have negative values in the raster, this will happen for any pixel that is less than 50% transparent (more than or equal to 50% opaque), because of how the 4 samples are packed into the int, and because int is a signed type in Java.
In this case, use:
int[] color = new int[3];
for (int i = 0; i < pixels.length; i++) {
// Assuming TYPE_INT_RGB, TYPE_INT_ARGB or TYPE_INT_ARGB_PRE
// For TYPE_INT_BGR, you need to reverse the colors.
// You seem to ignore alpha, is that correct?
color[0] = ((pixels[i] >> 16) & 0xff); // red;
color[1] = ((pixels[i] >> 8) & 0xff); // green;
color[2] = ( pixels[i] & 0xff); // blue;
// The rest of the computations...
}
Another possibility, is that you have created a custom type image (BufferedImage.TYPE_CUSTOM) that really uses a 32 bit unsigned int per sample. This is possible, however, int is still a signed entity in Java, so you need to mask off the sign bit. To complicate this a little, in Java -1 & 0xFFFFFFFF == -1 because any computation on an int will still be an int, unless you explicitly say otherwise (doing the same on a byte or short value would have "scaled up" to int). To get a positive value, you need to use a long value like this: -1 & 0xFFFFFFFFL (which is 4294967295).
In this case, use:
long[] color = new long[3];
for(int i = 0; i < pixels.length / pixel_size; i += pixel_size) {
// Somehow assuming BGR order in input, and RGB output (color)
// Still ignoring alpha
color[0] = (pixels[i + pixel_offset + 2] & 0xFFFFFFFFL); // red;
color[1] = (pixels[i + pixel_offset + 1] & 0xFFFFFFFFL); // green;
color[2] = (pixels[i + pixel_offset ] & 0xFFFFFFFFL); // blue;
// The rest of the computations...
}
I don't know what type of image you have, so I can't say for sure which one is the problem, but it's one of those. :-)
PS: BufferedImage.getAlphaRaster() is a possibly an expensive and also inaccurate way to tell if the image has alpha. It's better to just use image.getColorModel().hasAlpha(). See also hasAlpha vs getAlphaRaster.
Lets say I have an int[][] arrayA and an int[][] arrayB. At any given coordinate in this array lies an RGB value. What I want to do is merge the RGB values from arrayA and array2 into a new array, newArray, using a weighted average method.
So what I'm doing is extracting the red, green, and blue values from each RGB value like this:
curColA=RGB //suppose RGB is just the RGB in any given point
int curRedA = (curCol >> 16) & 0xFF;
int curGreenA = (curCol >> 8) & 0xFF;
int curBlueA= curCol & 0xFF;
I do the same for arrayB, and now I want to merge them. Here's where I'm having trouble. Do I just do newRed=(curRedA+curRedB)/2 or is there some other way to do this?
arrayA values: { { 0, 0x44, 0x5500, 0x660000 } };
arrayB values: { { 2, 4, 6, 8 } };
newArray expected values: { 0, 0x44, 6, 0x660000 } };
A weighted average is usually done something like:
newRed = (curRedA * redWeight + curRedB * (1 - redWeight));
...where redWeight is in the range [0, 1], and represents the weight (or bias) towards red values in the 'A' array. It also represents the inverse of the bias toward the red values in the 'B' array.
Can you please , explain to me (in words) what this code does?
thank you
My concerns are actually these two parts :
1)
double y_new = (double) (h * (128 - my_byte) / 256);
lines.add(new Line2D.Double(x, y_last, x, y_new));
y_last = y_new;
2) the for loop , I dont'understand...
what's 32768 ?
my_byte?
int numChannels = format.getChannels();
for (double x = 0; x < w && audioData != null; x++) {
int idx = (int) (frames_per_pixel * numChannels * x);
// se a 8 bit è immediato
if (format.getSampleSizeInBits() == 8) {
my_byte = (byte) audioData[idx];
} else {
my_byte = (byte) (128 * audioData[idx] / 32768);
Here's the code . It was taken from here :
http://www.koders.com/java/fid3508156A13C80A263E7CE65C4C9D6F5D8651AF5D.aspx?s=%22David+Anderson%22
(class Sampling Graph)
int frames_per_pixel = audioBytes.size() / format.getFrameSize() / w;
byte my_byte = 0;
double y_last = 0;
int numChannels = format.getChannels();
for (double x = 0; x < w && audioData != null; x++) {
// scegli quale byte visualizzare
int idx = (int) (frames_per_pixel * numChannels * x);
// se a 8 bit è immediato
if (format.getSampleSizeInBits() == 8) {
my_byte = (byte) audioData[idx];
} else {
my_byte = (byte) (128 * audioData[idx] / 32768);
}
double y_new = (double) (h * (128 - my_byte) / 256);
lines.add(new Line2D.Double(x, y_last, x, y_new));
y_last = y_new;
}
repaint();
Don't know if it helps, but
128 * someInt / 32768
is the same as
someInt << 7 >> 15
You should at the first place learn how a sound file is organized. But anyway..
double y_new = (double) (h * (128 - my_byte) / 256);
lines.add(new Line2D.Double(x, y_last, x, y_new));
y_last = y_new;
The Y positions of the lines that this example draws are representing the sample values of the sound file. As you may know one sample can be 8/16/32...bit. In this exaple they scale all bit values down to 8 (1 byte). The ne Y will have its center in the screen mid position (its a signed sound file). h is the screen hight-we want to scale it as if 127 is the screen top and -127 the screen lower pixel.
2) 32768 is the max value for a signed 16 bit integer. Thus, the max sample value for a 16 bit sound file. myByte: A sound file is saved as a byte stream. So evan if you use 16bit samples, you will have to create your integer (16bit, 32bin in Java) from the 2 bytes from the screen. In this example they work only with 8bit sample data, so if this example reads a 16bit sound file it will convert the sample values to 8 bit in this line:
my_byte = (byte) (128 * audioData[idx] / 32768);
So my byte holds the sample value of frame "idx".
double y_new = (double) (h * (128 - my_byte) / 256);
This line of code is part of a method to plot a sequence of bytes on a 'windows' (rectangle, coordinate system, as you like)
h := the height of the drawable area in pixel
my_byte := a byte [-128,127] (a 8 bit sample from an audio file?)
(128 - my_byte) converts the input [-128,127] to [256, 1]. Division by 256 transforms this range to [1, 1/256] and multiplying with the height h results in the expected mapping from [-128,127] --> [~0,h]
The following to lines are for drawing a line from the point representing the previous byte to the actual point. The actual point will then become the previous point for the next iteration.
my_byte = (byte) (128 * audioData[idx] / 32768);
This line of code is execute if the sample size is 16Bit. But we need an 8Bit value just for the plotting. An int in Java is 32 Bit, we have a 16 Bit sample, so the upper 16 Bit are 0, the lower 16 Bit store the audio sample value. The algorithm now shifts the value 7 positions to the left, then 15 to the right:
00000000 00000000 11111111 11111111
-> 00000000 01111111 11111111 10000000 // * 128, << 7
-> 00000000 00000000 00000000 11111111 // : 32768, >> 15
Honestly, i do not know why the author didn't just divide by 256 once, that should give the same result.
But anyway - the result is a byte that represents the eight most significant bits of the sample. Pretty inaccurate, by the way, because if you have a very quiete 16 bit recording you'll see nothing on the wave panel.