Visualization3RS.java revision bb362b27cbcccf8f845e6ec83d0540b477fece08
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.musicvis.vis3; 18 19import com.android.musicvis.R; 20import com.android.musicvis.RenderScriptScene; 21 22import android.graphics.Canvas; 23import android.graphics.Rect; 24import android.media.MediaPlayer; 25import android.os.Handler; 26import android.renderscript.Allocation; 27import android.renderscript.Element; 28import android.renderscript.Primitive; 29import android.renderscript.ProgramVertex; 30import android.renderscript.ScriptC; 31import android.renderscript.SimpleMesh; 32import android.renderscript.Type; 33import android.renderscript.Element.Builder; 34import android.util.Log; 35import android.view.SurfaceHolder; 36 37import java.util.TimeZone; 38 39class Visualization3RS extends RenderScriptScene { 40 41 private final Handler mHandler = new Handler(); 42 private final Runnable mDrawCube = new Runnable() { 43 public void run() { 44 updateWave(); 45 } 46 }; 47 private boolean mVisible; 48 49 static class WorldState { 50 public float yRotation; 51 public float mCenterX; 52 public float mCenterY; 53 } 54 WorldState mWorldState = new WorldState(); 55 private Type mStateType; 56 private Allocation mState; 57 58 private SimpleMesh mCubeMesh; 59 60 private Allocation mPointAlloc; 61 private float [] mPointData = new float[512*4]; 62 63 private Allocation mLineIdxAlloc; 64 private int [] mIndexData = new int[512]; // we'll pack 2 indices per int 65 66 private ProgramVertex mPVBackground; 67 private ProgramVertex.MatrixAllocation mPVAlloc; 68 69 private short [] mVizData = new short[512]; 70 private short [] mAnalyzer = new short[512]; 71 72 private static final int RSID_STATE = 0; 73 private static final int RSID_POINTS = 1; 74 private static final int RSID_LINES = 2; 75 private static final int RSID_PROGRAMVERTEX = 3; 76 77 78 Visualization3RS(int width, int height) { 79 super(width, height); 80 mWidth = width; 81 mHeight = height; 82 } 83 84 @Override 85 public void resize(int width, int height) { 86 super.resize(width, height); 87 mWorldState.mCenterX = mWidth / 2; 88 mWorldState.mCenterY = mHeight / 2; 89 if (mPVAlloc != null) { 90 Log.i("@@@@@", "resized to " + mWidth + "x" + mHeight); 91 mPVAlloc.setupProjectionNormalized(mWidth, mHeight); 92 } 93 } 94 95 @Override 96 protected ScriptC createScript() { 97 98 // Create a renderscript type from a java class. The specified name doesn't 99 // really matter; the name by which we refer to the object in RenderScript 100 // will be specified later. 101 mStateType = Type.createFromClass(mRS, WorldState.class, 1, "WorldState"); 102 // Create an allocation from the type we just created. 103 mState = Allocation.createTyped(mRS, mStateType); 104 // set our java object as the data for the renderscript allocation 105 mWorldState.yRotation = (-0.5f) * 2 * 180 / (float) Math.PI; 106 mState.data(mWorldState); 107 108 /* 109 * Now put our model in to a form that renderscript can work with: 110 * - create a buffer of floats that are the coordinates for the points that define the cube 111 * - create a buffer of integers that are the indices of the points that form lines 112 * - combine the two in to a mesh 113 */ 114 115 // First set up the coordinate system and such 116 ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null); 117 mPVBackground = pvb.create(); 118 mPVBackground.setName("PVBackground"); 119 mPVAlloc = new ProgramVertex.MatrixAllocation(mRS); 120 mPVBackground.bindAllocation(mPVAlloc); 121 mPVAlloc.setupProjectionNormalized(mWidth, mHeight); 122 123 // Start creating the mesh 124 final SimpleMesh.Builder meshBuilder = new SimpleMesh.Builder(mRS); 125 126 // Create the Element for the points 127 Builder elementBuilder = new Builder(mRS); 128 // By specifying a prefix, even an empty one, the members will be accessible 129 // in the renderscript. If we just called addFloatXYZ(), the members would be 130 // unnamed in the renderscript struct definition. 131 elementBuilder.addFloatXY(""); 132 final Element vertexElement = elementBuilder.create(); 133 final int vertexSlot = meshBuilder.addVertexType(vertexElement, mPointData.length / 2); 134 // Specify the type and number of indices we need. We'll allocate them later. 135 meshBuilder.setIndexType(Element.INDEX_16(mRS), mIndexData.length * 2); 136 // This will be a line mesh 137 meshBuilder.setPrimitive(Primitive.LINE); 138 139 // Create the Allocation for the vertices 140 mCubeMesh = meshBuilder.create(); 141 mCubeMesh.setName("CubeMesh"); 142 mPointAlloc = mCubeMesh.createVertexAllocation(vertexSlot); 143 mPointAlloc.setName("PointBuffer"); 144 145 // Create the Allocation for the indices 146 mLineIdxAlloc = mCubeMesh.createIndexAllocation(); 147 148 // Bind the allocations to the mesh 149 mCubeMesh.bindVertexAllocation(mPointAlloc, 0); 150 mCubeMesh.bindIndexAllocation(mLineIdxAlloc); 151 152 /* 153 * put the vertex and index data in their respective buffers 154 */ 155 updateWave(); 156 for(int i = 0; i < mIndexData.length; i ++) { 157 mIndexData[i] = (i*2) | ((i*2+1) << 16); 158 } 159 160 /* 161 * upload the vertex and index data 162 */ 163 mPointAlloc.data(mPointData); 164 mPointAlloc.uploadToBufferObject(); 165 mLineIdxAlloc.data(mIndexData); 166 mLineIdxAlloc.uploadToBufferObject(); 167 168 // Time to create the script 169 ScriptC.Builder sb = new ScriptC.Builder(mRS); 170 // Specify the name by which to refer to the WorldState object in the 171 // renderscript. 172 sb.setType(mStateType, "State", RSID_STATE); 173 sb.setType(mCubeMesh.getVertexType(0), "Points", RSID_POINTS); 174 // this crashes when uncommented 175 //sb.setType(mCubeMesh.getIndexType(), "Lines", RSID_LINES); 176 sb.setScript(mResources, R.raw.spectrum); 177 sb.setRoot(true); 178 179 ScriptC script = sb.create(); 180 script.setClearColor(0.0f, 0.0f, 0.0f, 1.0f); 181 script.setTimeZone(TimeZone.getDefault().getID()); 182 183 script.bindAllocation(mState, RSID_STATE); 184 script.bindAllocation(mPointAlloc, RSID_POINTS); 185 script.bindAllocation(mLineIdxAlloc, RSID_LINES); 186 script.bindAllocation(mPVAlloc.mAlloc, RSID_PROGRAMVERTEX); 187 188 return script; 189 } 190 191 @Override 192 public void setOffset(float xOffset, float yOffset, int xPixels, int yPixels) { 193 // update our state, then push it to the renderscript 194 mWorldState.yRotation = (xOffset - 0.5f) * 360; // rotate -180 to +180 195 mState.data(mWorldState); 196 } 197 198 @Override 199 public void start() { 200 super.start(); 201 mVisible = true; 202 updateWave(); 203 } 204 205 @Override 206 public void stop() { 207 super.stop(); 208 mVisible = true; 209 } 210 211 void updateWave() { 212 mHandler.removeCallbacks(mDrawCube); 213 if (mVisible) { 214 mHandler.postDelayed(mDrawCube, 20); 215 } 216 217 int len = MediaPlayer.snoop(mVizData, 1); 218 if (len == 0) { 219 // there was no new data, so make the visualization go away 220 for (int i = 0; i < mVizData.length; i++) { 221 mVizData[i] = 0; 222 } 223 } 224 225 // we always get 256 points 226 for (int i=0; i < 256; i++) { 227 short newval = (short)(mVizData[i] * (i/16+2)); 228 short oldval = mAnalyzer[i * 2]; 229 if (newval >= oldval - 800) { 230 // use new high value 231 } else { 232 newval = (short)(oldval - 800); 233 } 234 // double the data, since the fft only returns 256 points 235 mAnalyzer[i * 2] = mAnalyzer[i * 2 + 1] = newval; 236 } 237 238 int outlen = mPointData.length / 4; 239 int half = outlen / 2; 240 for(int i = 0; i < outlen; i++) { 241 mPointData[i*4] = i - half; 242 mPointData[i*4+1] = mAnalyzer[i] / 100; 243 mPointData[i*4+2] = i - half; 244 mPointData[i*4+3] = -mAnalyzer[i] / 100; 245 } 246 mPointAlloc.data(mPointData); 247 248 } 249 250} 251