Im trying to build a simple Music Visualisation App which just should resize a Circle. So if the Music Part which is currently playing is loud it should get bigger and if not it should get smaller.
To Visualize the Circle I just created a custom View Class which draws the circle in the onDraw Method.
To get the informations out of the current Audio, I found the Visualizer Class of Android and also used the setDataCaptureListener.
mVisualizer = new Visualizer(mMediaPlayer.getAudioSessionId());
mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[0]);
mVisualizer.setDataCaptureListener(
new Visualizer.OnDataCaptureListener() {
public void onWaveFormDataCapture(Visualizer visualizer,byte[] bytes, int samplingRate) {
mVisualizerView.updateVisualizer(bytes);
}
public void onFftDataCapture(Visualizer visualizer,byte[] bytes, int samplingRate) {
}
}, (int)(Visualizer.getMaxCaptureRate() / 1.5), true, false);
But my Problem is that I don't really know how I use the byte Array which is given back, to find out the music change in general (got louder or not ?).
I just tried to get the average of the array but this gives me completely bad results. The circle changed his size like it is on drugs. So I thought maybe the array has too many outlined/extreme values (which was true) so I calculated the median of the array. This gaved me better results but still isn't what I want. It's not very smooth and it's to complex. I always have to sort the array which is not really efficient. What am I thinking wrong ?
Im really a beginner in this AudioFX section and Im completely sorry If this is a dumb question and attempt of me.
Thank you for your help !
EDIT:
private float schwelle = 5000;
private float last = 0;
...
float summe = 0;
for (Byte currentByte: mBytes)
summe += currentByte;
if (summe > schwelle && summe > last)
{
last = summe; //make it bigger
}
else {
last -= 100; //make circle smaller
}
canvas.drawCircle(getWidth()/2,getHeight()/2,last / 100,mForePaint);
A really good git project is https://github.com/felixpalmer/android-visualizer.
I myself came up with this:(it's a lot simple than the git solution)
You can use the values of the array to draw the the waveform on the outline of a circle using trigonometry, and make the start radius of the circle bigger if the sum of the array is bigger than certain treshhold:
class StarWaveformRenderer implements Renderer {
private Paint p = new Paint();
private static final int BOOST_TRASH_HOLD = 10000;
private float stretchFade = 1; //circle fades after a prominent beat
#Override
public void render(Canvas canvas, byte[] data) {
if (data == null || data.length == 0)
return;
int centerX = canvas.getWidth() / 2;
int centerY = canvas.getHeight() / 2;
float stretch = stretchFade;
int sum = RenderUtils.sum(data);
p.setColor((p.getColor() + sum / 2)); //change color of circle
if (sum > BOOST_TRASH_HOLD) {//prominent beat
stretch = (float) Math.min(canvas.getWidth(), canvas.getHeight()) / Byte.MAX_VALUE / 3; //maximum
stretchFade = stretch;
}
double radDif = 2 * Math.PI / data.length; //the angle between each element of the array
double radPos = 0;
float lX = (float) Math.cos(radPos) * data[0] + centerX;
float lY = (float) Math.sin(radPos) * data[0] + centerY;
float cX;
float cY;
for (byte b : data) {
cX = (float) Math.cos(radPos) * b * stretch + centerX;
cY = (float) Math.sin(radPos) * b * stretch + centerY;//calculate position of outline, stretch indicates promince of the beat
canvas.drawLine(lX, lY, cX, cY, p);
lX = cX;
lY = cY;
radPos += radDif;
}
stretchFade = Math.max(1, stretchFade / 1.2f);//beat fades out
}
}
You can programm your own renderes and let the user select which one he wants to use. Just pass the array from onWaveformDataCapture to the onRender method.
Utils for analysing the waveform (the amplitude is stored kind of weird):
class RenderUtils {
private static final byte SHIFT = Byte.MAX_VALUE;
static int sum(byte[] data) {
int sum = 0;
for (byte b : data)
sum += b;
return sum;
}
static int toAmplitude(byte b) {
return b > 0 ? b + SHIFT : -b;//+127=high positive;+1=low positive;-127=low negative;-1=high negative
}
static float toAmplitude(float f) {
return f > 0 ? f + SHIFT : -f;//+127=high positive;+1=low positive;-127=low negative;-1=high negative
}
}
Related
I am trying to floodfill a bitmap using Renderscript. and my renderscript file progress.rs is
#pragma version(1)
#pragma rs java_package_name(com.intel.sample.androidbasicrs)
rs_allocation input;
int width;
int height;
int xTouchApply;
int yTouchApply;
static int same(uchar4 pixel, uchar4 in);
uchar4 __attribute__((kernel)) root(const uchar4 in, uint32_t x, uint32_t y) {
uchar4 out = in;
rsDebug("Process.rs : image width: ", width);
rsDebug("Process.rs : image height: ", height);
rsDebug("Process.rs : image pointX: ", xTouchApply);
rsDebug("Process.rs : image pointY: ", yTouchApply);
if(xTouchApply >= 0 && xTouchApply < width && yTouchApply >=0 && yTouchApply < height){
// getting touched pixel
uchar4 pixel = rsGetElementAt_uchar4(input, xTouchApply, yTouchApply);
rsDebug("Process.rs : getting touched pixel", 0);
// resets the pixel stack
int topOfStackIndex = 0;
// creating pixel stack
int pixelStack[width*height];
// Pushes the touched pixel onto the stack
pixelStack[topOfStackIndex] = xTouchApply;
pixelStack[topOfStackIndex+1] = yTouchApply;
topOfStackIndex += 2;
//four way stack floodfill algorithm
while(topOfStackIndex>0){
rsDebug("Process.rs : looping while", 0);
// Pops a pixel from the stack
int x = pixelStack[topOfStackIndex - 2];
int y1 = pixelStack[topOfStackIndex - 1];
topOfStackIndex -= 2;
while (y1 >= 0 && same(rsGetElementAt_uchar4(input, x, y1), pixel)) {
y1--;
}
y1++;
int spanLeft = 0;
int spanRight = 0;
while (y1 < height && same(rsGetElementAt_uchar4(input, x, y1), pixel)) {
rsDebug("Process.rs : pointX: ", x);
rsDebug("Process.rs : pointY: ", y1);
float3 outPixel = dot(f4.rgb, channelWeights);
out = rsPackColorTo8888(outPixel);
// conditions to traverse skipPixels to check threshold color(Similar color)
if (!spanLeft && x > 0 && same(rsGetElementAt_uchar4(input, x - 1, y1), pixel)) {
// Pixel to the left must also be changed, pushes it to the stack
pixelStack[topOfStackIndex] = x - 1;
pixelStack[topOfStackIndex + 1] = y1;
topOfStackIndex += 2;
spanLeft = 1;
} else if (spanLeft && !same(rsGetElementAt_uchar4(input, x - 1, y1), pixel)) {
// Pixel to the left has already been changed
spanLeft = 0;
}
// conditions to traverse skipPixels to check threshold color(Similar color)
if (!spanRight && x < width - 1 && same(rsGetElementAt_uchar4(input, x + 1, y1), pixel)) {
// Pixel to the right must also be changed, pushes it to the stack
pixelStack[topOfStackIndex] = x + 1;
pixelStack[topOfStackIndex + 1] = y1;
topOfStackIndex += 2;
spanRight = 1;
} else if (spanRight && x < width - 1 && !same(rsGetElementAt_uchar4(input, x + 1, y1), pixel)) {
// Pixel to the right has already been changed
spanRight = 0;
}
y1++;
}
}
}
return out;
}
static int same(uchar4 px, uchar4 inPx){
int isSame = 0;
if((px.r == inPx.r) && (px.g == inPx.g) && (px.b == inPx.b) && (px.a == inPx.a)) {
isSame = 1;
// rsDebug("Process.rs : matching pixel: ", isSame);
} else {
isSame = 0;
}
// rsDebug("Process.rs : matching pixel: ", isSame);
return isSame;
}
and my Activity's code is:
inputBitmap = Bitmap.createScaledBitmap(inputBitmap, displayWidth, displayHeight, false);
// Create an allocation (which is memory abstraction in the RenderScript)
// that corresponds to the inputBitmap.
allocationIn = Allocation.createFromBitmap(
rs,
inputBitmap,
Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT
);
allocationOut = Allocation.createTyped(rs, allocationIn.getType());
int imageWidth = inputBitmap.getWidth();
int imageHeight = inputBitmap.getHeight();
script.set_width(imageWidth);
script.set_height(imageHeight);
script.set_input(allocationIn);
//....
//....
// and my onTouchEvent Code is
script.set_xTouchApply(xTouchApply);
script.set_yTouchApply(yTouchApply);
// Run the script.
script.forEach_root(allocationIn, allocationOut);
allocationOut.copyTo(outputBitmap);
when I touched bitmap it is showing Application not responding. It is because of root method is calling for every pixels. How can I optimize this code. And how can I compare two uchar4 variables in Renderscript? How can I improve my same method? Or How can I find similar neighbor pixels using threshold value? I got stuck. Please guys help me.
I don't have much knowledge of c99 programming language and Renderscript. Can you guys debug my renderscript code. and please tell me what's wrong in this code. Or can I improve this renderscript code to floodfill the bitmap. Any help will be appreciated And sorry for my poor English ;-) . Thanks
Renderscript is Android's front-end to GPU-instructions. And it is extremely good if you want to perform operations on each pixel because it uses the massive GPU-parallelism-capabilities. So, you can run an operation on each pixel. For this purpose, you start a program in Renderscript with sth like "for all pixels, do the following".
The flood fill algorithm though cannot run in such a parallel environment because you only know which pixel to paint after painting another pixel before it. This is not only true for renderscript but all GPU-related libraries, like CUDA or others.
I am trying to draw Circle with texture on it which should be stretched on all vertices.
The problem is that the result i get looks like this:
http://s14.postimg.org/3wyb74469/image.png
I have tried to draw triangle fan as it need to be , first coordinates at 0,0,0
And rest as needed:
http://escience.anu.edu.au/lecture/cg/surfaceModeling/image/surfaceModeling015.png
Also here is the same question and I couldn't get answer for my problem from it :
OpenGL ES, add texture to circle
Loading circle vertices coordinates function:
private final int mVerticesDataSize = 3;
private final int mNumberOfVertices = 180;
private final int mBytesPerFloat = 4;
private float[] vertices;
private FloatBuffer mVerticesBuff;
public void loadCircleVerticesBuff(Context mActivityContext){
mVerticesBuff = ByteBuffer.allocateDirect(mNumberOfVertices * mVerticesDataSize * mBytesPerFloat).order(ByteOrder.nativeOrder()).asFloatBuffer();
vertices = new float[mNumberOfVertices * mVerticesDataSize];
float theta = 0;
for (int i = 0; i < (mNumberOfVertices * mVerticesDataSize); i += 3) {
vertices[i] = (float) (((float) 5*Math.cos(theta)));
vertices[i + 1] = (float) ((float) 5*Math.sin(theta));
vertices[i + 2] = 0;
theta += Math.PI / 90;
}
mVerticesBuff.put(vertices);
mVerticesBuff.position(0);
}
Loading circle texture coordinates function:
private final int mTextureCoordinateDataSize = 3;
public void loadCircleTextureBuff(){
mCircleTextureCoordinatesBuff = ByteBuffer.allocateDirect(mNumberOfVertices * mTextureCoordinateDataSize * mBytesPerFloat).order(ByteOrder.nativeOrder()).asFloatBuffer();
mCircleTextureCoordinatesBuff.put(vertices);
mCircleTextureCoordinatesBuff.position(0);
}
The opengl function used to draw is :
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, mNumberOfVertices);
So solution found (: 1 very very importent thing i missed up / or openGL missed up.
Texture coordinates can be only in range of 0 to 1 .
So here is the trick/solution :
//Build vertices :
vertices[i] = (float) (((float) raduis*Math.cos(theta)))+raduis;
vertices[i + 1] = (float) ((float) raduis*Math.sin(theta))+raduis;
// Build texture :
for (int i = 0; i < (mNumberOfVertices * mVerticesDataSize); i += 3) {
vertices[i] = (vertices[i])/(raduis*2);
vertices[i + 1] = (vertices[i+1])/(raduis*2);
vertices[i + 2] = 0;
}
And here is the res :
http://s2.postimg.org/tno4jr4y1/image.png
Dont forget to flip texture vertices as i forgot (:
I'm in a tremendous bind with a last minute request on a consulting project I'm working on.
Essentially here is what I am trying to accomplish:
I have a surfaceview that draws a series of randomly sized circles. Each circle can have a radius from 50-100.
The x,y values are randomly generated along with a random radius
Each circle is created as an object representing that circle (x, y coord's and radius) and it is added to a list.
Once they are all created they are drawn.
The problem is I want to make sure none of these circles overlap.
I'm struggling a bit. This seems like it's shouldn't be all that difficult but it is for me unfortunately.
Here's my code so far (I know it's not close...be kind):
x = 100 + (int) (Math.random() * (mCanvasWidth - 200));
y = 100 + (int) (Math.random() * (mCanvasHeight - 200));
radius = 50 + (int) (Math.random() * 99);
color[0] = (float) (Math.random() * 360);
color[1] = 1;
color[2] = 1;
String radVal = String.valueOf(radius);
circle circ = new circle(x, y, radius, Color.HSVToColor(128, color), radVal);
boolean addit = true;
for (dot d : Dots) {
int leftSide = d.get_x() - radius;
int rightSide = d.get_x() + radius;
int xBoundary = x + radius;
int yBoundary = y + radius;
int exist_xLeft = d.get_x() - d.get_radius();
int exist_xRight = d.get_x() + d.get_radius();
int exist_yTop = d.get_y() - d.get_radius();
int exist_yBottom = d.get_y() + d.get_radius();
if ((xBoundary > exist_xLeft) && (xBoundary < exist_xRight))
{
if (yBoundary > (exist_yTop) && (yBoundary < exist_yBottom)) {
addit = false;
break;
}
}
}
if (addit)
circles.add(mdot);
if (circles.size() >= 5)
running = false;
Then it iterates the circles list and draws them to the canvas.
Any suggestions on where I'm failing in the collision detection?
You can detect if 2 circles are colliding like this:
Given:
centerpoints cx1,cy1 & cx2,cy2
and given radii r1 & r2,
Then you can determine if the 2 circles are colliding:
areColliding=((cx2-cx1)*(cx2-cx1)+(cy2-cy1)*(cy2-cy1))<((r1+r2)*(r1+r2));
What's up guys, I need a little help with this one. I'm trying to achieve a simple(but not really) folding animation on a listview that is being scrolled. Basically, I'm attempting to fold the listview's first visible child backward as if a sheet of paper is being folded downward along the X axis. This goes on on continuously as the user scrolls up and down the list. This is my first time playing around with Matrix animations and Android's camera from the graphics api, so I'm definitely off the mark here.
This is the effect I'm trying to achieve
And this is the effect I'm getting.
I want the animation to begin at the origin(0,0) but both the left and right side, animating from the top of the list item instead of the upper left corner. I'm not very familiar with matrix translations or animations so If anyone much more experience with these techniques than myself can shed some knowledge, it'll be greatly appreciated.
Basically I'm overriding the onDrawChild method of ListView, grabbing the child's bitmap from a drawing cache, and using a matrix to perform the animation. The lighting and camera implementation is code that I took from another sample app in order to get the animation to look as 3D as possible.
I tried playing around with the ListView animations library, but without much luck. I also tried to hack together a solution using code from the developer guides here that uses object animators to achieve a nice little card flip animation, but it started feeling a bit hacky and I couldn't quite get it the way I wanted.
Here's my current implementation. If anyone can shed some light or direction on this one, or maybe if anyone wrote an awesome library that I didn't come across on my searches, please feel free to share. Thanks
#Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
View first = getChildAt(0);
if (child == first) {
if (child.getTop() < 0) {
Bitmap bitmap = getChildDrawingCache(child);
final int top = child.getTop();
child.getRight();
child.getBottom();
child.getLeft();
final int childCenterY = child.getHeight() / 2;
// final int childCenterX = child.getWidth() / 2;
final int parentCenterY = getHeight() / 2; // center point of
// child relative to list
final int absChildCenterY = child.getTop() + childCenterY;
// final int bottom = child.getBottom();
// distance of child center to the list center final int
int distanceY = parentCenterY - absChildCenterY;
final int r = getHeight() / 2;
if (mAnimate) {
prepareMatrix(mMatrix, distanceY, r);
mMatrix.preTranslate(0, top);
mMatrix.postTranslate(0, -top);
}
canvas.drawBitmap(bitmap, mMatrix, mPaint);
}
else {
super.drawChild(canvas, child, drawingTime);
}
} else {
super.drawChild(canvas, child, drawingTime);
}
return false;
}
private void prepareMatrix(final Matrix outMatrix, int distanceY, int r) { // clip
// the
// distance
final int d = Math.min(r, Math.abs(distanceY)); //
// circle formula
final float translateZ = (float) Math.sqrt((r * r) - (d * d));
double radians = Math.acos((float) d / r);
double degree = 45 - (180 / Math.PI) * radians;
// double degree = -180;
mCamera.save();
mCamera.translate(0, 0, r - translateZ);
mCamera.rotateX((float) degree);
if (distanceY < 0) {
degree = 360 - degree;
}
mCamera.rotateY((float) degree);
mCamera.getMatrix(outMatrix);
mCamera.restore();
// highlight elements in the middle
mPaint.setColorFilter(calculateLight((float) degree));
}
private Bitmap getChildDrawingCache(final View child) {
Bitmap bitmap = child.getDrawingCache();
if (bitmap == null) {
child.setDrawingCacheEnabled(true);
child.buildDrawingCache();
bitmap = child.getDrawingCache();
}
return bitmap;
}
private LightingColorFilter calculateLight(final float rotation) {
final double cosRotation = Math.cos(Math.PI * rotation / 180);
int intensity = AMBIENT_LIGHT + (int) (DIFFUSE_LIGHT * cosRotation);
int highlightIntensity = (int) (SPECULAR_LIGHT * Math.pow(cosRotation,
SHININESS));
if (intensity > MAX_INTENSITY) {
intensity = MAX_INTENSITY;
}
if (highlightIntensity > MAX_INTENSITY) {
highlightIntensity = MAX_INTENSITY;
}
final int light = Color.rgb(intensity, intensity, intensity);
final int highlight = Color.rgb(highlightIntensity, highlightIntensity,
highlightIntensity);
return new LightingColorFilter(light, highlight);
}
JazzyListView
has a lot of stuff that's similar to what you want if not exactly what you want. Take a look at how they're defined under jazzy effect and mix and match. I think reverse fly or maybe flip is close to what you want.
I've a custom view which draws a running graph - some magnitude versus time. Now I want to implement a custom scroll bar for this so that I can view past data which are offscreen. The data is available to me. I just need the %offset selection by the user.
Any help/suggestions on implementation would be v helpful.
Code Snippet from my Custom view's onDraw method
public void onDraw(Canvas canvas) {
int totalpts = data.size();
scale = getWidth() / (float) maxpoints;
List<Data> display = new ArrayList<Data>();
int initial = 1;
if (totalpts > maxpoints) {
initial = totalpts - maxpoints;
display = data.subList(initial, data.size() - 1);
} else {
display = data;
}
int size = display.size();
Data start = null;
float x1 = 0, x2 = 0, x = 0;
if (size > 1) {
x1 = getWidth();
start = display.get(display.size() - 1);
for (int i = display.size() - 1; i >= 0; i--) {
Data stop = display.get(i);
x = x1;
x1 -= (stop.x * scale / 1000);
canvas.drawLine(x, start.Y, x1, stop.Y, paint1);
start = stop;
}
}
}
Try putting your custom control inside a HorizonatalScrollView (assuming you want it to scroll horizontally), use ScrollView otherwise), setting the width of your control to "WRAP_CONTENT", and the HoizontalScrollView to "FILL_PARENT". Without seeing the code for your custom view, it's difficult to know whether you might need to do some tinkering with the width calculation to get this working.