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 17 package com.android.musicvis; 18 19 import static android.renderscript.Element.RGB_565; 20 import static android.renderscript.ProgramFragment.EnvMode.REPLACE; 21 import static android.renderscript.Sampler.Value.LINEAR; 22 import static android.renderscript.Sampler.Value.WRAP; 23 24 import android.media.MediaPlayer; 25 import android.os.Handler; 26 import android.os.SystemClock; 27 import android.renderscript.Allocation; 28 import android.renderscript.Element; 29 import android.renderscript.Primitive; 30 import android.renderscript.ProgramFragment; 31 import android.renderscript.ProgramVertex; 32 import android.renderscript.Sampler; 33 import android.renderscript.ScriptC; 34 import android.renderscript.SimpleMesh; 35 import android.renderscript.Type; 36 import android.renderscript.Element.Builder; 37 import android.util.Log; 38 39 import java.util.TimeZone; 40 41 public class GenericWaveRS extends RenderScriptScene { 42 43 private final Handler mHandler = new Handler(); 44 private final Runnable mDrawCube = new Runnable() { 45 public void run() { 46 updateWave(); 47 } 48 }; 49 private boolean mVisible; 50 private int mTexId; 51 52 protected static class WorldState { 53 public float yRotation; 54 public int idle; 55 public int waveCounter; 56 public int width; 57 } 58 protected WorldState mWorldState = new WorldState(); 59 private Type mStateType; 60 protected Allocation mState; 61 62 private SimpleMesh mCubeMesh; 63 64 protected Allocation mPointAlloc; 65 // 1024 lines, with 4 points per line (2 space, 2 texture) each consisting of x and y, 66 // so 8 floats per line. 67 protected float [] mPointData = new float[1024*8]; 68 69 private Allocation mLineIdxAlloc; 70 // 2 indices per line 71 private short [] mIndexData = new short[1024*2]; 72 73 private ProgramVertex mPVBackground; 74 private ProgramVertex.MatrixAllocation mPVAlloc; 75 76 protected short [] mVizData = new short[1024]; 77 78 private ProgramFragment mPfBackground; 79 private Sampler mSampler; 80 private Allocation mTexture; 81 82 private static final int RSID_STATE = 0; 83 private static final int RSID_POINTS = 1; 84 private static final int RSID_LINES = 2; 85 private static final int RSID_PROGRAMVERTEX = 3; 86 87 GenericWaveRS(int width, int height, int texid)88 protected GenericWaveRS(int width, int height, int texid) { 89 super(width, height); 90 mTexId = texid; 91 mWidth = width; 92 mHeight = height; 93 // the x, s and t coordinates don't change, so set those now 94 int outlen = mPointData.length / 8; 95 int half = outlen / 2; 96 for(int i = 0; i < outlen; i++) { 97 mPointData[i*8] = i - half; // start point X (Y set later) 98 mPointData[i*8+2] = 0; // start point S 99 mPointData[i*8+3] = 0; // start point T 100 mPointData[i*8+4] = i - half; // end point X (Y set later) 101 mPointData[i*8+6] = 1.0f; // end point S 102 mPointData[i*8+7] = 0f; // end point T 103 } 104 } 105 106 @Override resize(int width, int height)107 public void resize(int width, int height) { 108 super.resize(width, height); 109 mWorldState.width = width; 110 if (mPVAlloc != null) { 111 mPVAlloc.setupProjectionNormalized(mWidth, mHeight); 112 } 113 } 114 115 @Override createScript()116 protected ScriptC createScript() { 117 118 // Create a renderscript type from a java class. The specified name doesn't 119 // really matter; the name by which we refer to the object in RenderScript 120 // will be specified later. 121 mStateType = Type.createFromClass(mRS, WorldState.class, 1, "WorldState"); 122 // Create an allocation from the type we just created. 123 mState = Allocation.createTyped(mRS, mStateType); 124 // set our java object as the data for the renderscript allocation 125 mWorldState.yRotation = 0.0f; 126 mWorldState.width = mWidth; 127 mState.data(mWorldState); 128 129 /* 130 * Now put our model in to a form that renderscript can work with: 131 * - create a buffer of floats that are the coordinates for the points that define the cube 132 * - create a buffer of integers that are the indices of the points that form lines 133 * - combine the two in to a mesh 134 */ 135 136 // First set up the coordinate system and such 137 ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null); 138 mPVBackground = pvb.create(); 139 mPVBackground.setName("PVBackground"); 140 mPVAlloc = new ProgramVertex.MatrixAllocation(mRS); 141 mPVBackground.bindAllocation(mPVAlloc); 142 mPVAlloc.setupProjectionNormalized(mWidth, mHeight); 143 144 // Start creating the mesh 145 final SimpleMesh.Builder meshBuilder = new SimpleMesh.Builder(mRS); 146 147 // Create the Element for the points 148 Builder elementBuilder = new Builder(mRS); 149 // By specifying a prefix, even an empty one, the members will be accessible 150 // in the renderscript. If we just called addFloatXYZ(), the members would be 151 // unnamed in the renderscript struct definition. 152 elementBuilder.addFloatXY(""); 153 elementBuilder.addFloatST(""); 154 final Element vertexElement = elementBuilder.create(); 155 final int vertexSlot = meshBuilder.addVertexType(vertexElement, mPointData.length / 4); 156 // Specify the type and number of indices we need. We'll allocate them later. 157 meshBuilder.setIndexType(Element.INDEX_16(mRS), mIndexData.length); 158 // This will be a line mesh 159 meshBuilder.setPrimitive(Primitive.LINE); 160 161 // Create the Allocation for the vertices 162 mCubeMesh = meshBuilder.create(); 163 mCubeMesh.setName("CubeMesh"); 164 mPointAlloc = mCubeMesh.createVertexAllocation(vertexSlot); 165 mPointAlloc.setName("PointBuffer"); 166 167 // Create the Allocation for the indices 168 mLineIdxAlloc = mCubeMesh.createIndexAllocation(); 169 170 // Bind the allocations to the mesh 171 mCubeMesh.bindVertexAllocation(mPointAlloc, 0); 172 mCubeMesh.bindIndexAllocation(mLineIdxAlloc); 173 174 /* 175 * put the vertex and index data in their respective buffers 176 */ 177 updateWave(); 178 for(int i = 0; i < mIndexData.length; i ++) { 179 mIndexData[i] = (short) i; 180 } 181 182 /* 183 * upload the vertex and index data 184 */ 185 mPointAlloc.data(mPointData); 186 mPointAlloc.uploadToBufferObject(); 187 mLineIdxAlloc.data(mIndexData); 188 mLineIdxAlloc.uploadToBufferObject(); 189 190 /* 191 * load the texture 192 */ 193 mTexture = Allocation.createFromBitmapResourceBoxed(mRS, mResources, mTexId, RGB_565(mRS), false); 194 mTexture.setName("Tlinetexture"); 195 mTexture.uploadToTexture(0); 196 197 /* 198 * create a program fragment to use the texture 199 */ 200 Sampler.Builder samplerBuilder = new Sampler.Builder(mRS); 201 samplerBuilder.setMin(LINEAR); 202 samplerBuilder.setMag(LINEAR); 203 samplerBuilder.setWrapS(WRAP); 204 samplerBuilder.setWrapT(WRAP); 205 mSampler = samplerBuilder.create(); 206 207 ProgramFragment.Builder builder = new ProgramFragment.Builder(mRS, null, null); 208 builder.setTexEnable(true, 0); 209 builder.setTexEnvMode(REPLACE, 0); 210 mPfBackground = builder.create(); 211 mPfBackground.setName("PFBackground"); 212 mPfBackground.bindSampler(mSampler, 0); 213 214 // Time to create the script 215 ScriptC.Builder sb = new ScriptC.Builder(mRS); 216 // Specify the name by which to refer to the WorldState object in the 217 // renderscript. 218 sb.setType(mStateType, "State", RSID_STATE); 219 sb.setType(mCubeMesh.getVertexType(0), "Points", RSID_POINTS); 220 // this crashes when uncommented 221 //sb.setType(mCubeMesh.getIndexType(), "Lines", RSID_LINES); 222 sb.setScript(mResources, R.raw.waveform); 223 sb.setRoot(true); 224 225 ScriptC script = sb.create(); 226 script.setClearColor(0.0f, 0.0f, 0.0f, 1.0f); 227 script.setTimeZone(TimeZone.getDefault().getID()); 228 229 script.bindAllocation(mState, RSID_STATE); 230 script.bindAllocation(mPointAlloc, RSID_POINTS); 231 script.bindAllocation(mLineIdxAlloc, RSID_LINES); 232 script.bindAllocation(mPVAlloc.mAlloc, RSID_PROGRAMVERTEX); 233 234 return script; 235 } 236 237 @Override setOffset(float xOffset, float yOffset, float xStep, float yStep, int xPixels, int yPixels)238 public void setOffset(float xOffset, float yOffset, 239 float xStep, float yStep, int xPixels, int yPixels) { 240 // update our state, then push it to the renderscript 241 242 if (xStep <= 0.0f) { 243 xStep = xOffset / 2; // originator didn't set step size, assume we're halfway 244 } 245 mWorldState.yRotation = (xOffset / xStep) * 180; // rotate 180 degrees per screen 246 mState.data(mWorldState); 247 } 248 249 @Override start()250 public void start() { 251 super.start(); 252 mVisible = true; 253 // Preroll the MediaPlayer so we don't get a spurious 'idle' 254 MediaPlayer.snoop(mVizData, 0); 255 SystemClock.sleep(200); 256 updateWave(); 257 } 258 259 @Override stop()260 public void stop() { 261 super.stop(); 262 mVisible = false; 263 updateWave(); 264 } 265 update()266 public void update() { 267 } 268 updateWave()269 void updateWave() { 270 mHandler.removeCallbacks(mDrawCube); 271 if (!mVisible) { 272 return; 273 } 274 mHandler.postDelayed(mDrawCube, 20); 275 update(); 276 mWorldState.waveCounter++; 277 mState.data(mWorldState); 278 } 279 280 } 281