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