Home | History | Annotate | Download | only in musicvis
      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