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