Forgot some files in my previous checkin
diff --git a/src/com/android/musicvis/vis5/Visualization5RS.java b/src/com/android/musicvis/vis5/Visualization5RS.java
new file mode 100644
index 0000000..e118b64
--- /dev/null
+++ b/src/com/android/musicvis/vis5/Visualization5RS.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.musicvis.vis5;
+
+import static android.renderscript.ProgramFragment.EnvMode.REPLACE;
+import static android.renderscript.Sampler.Value.LINEAR;
+import static android.renderscript.Sampler.Value.WRAP;
+
+import com.android.musicvis.R;
+import com.android.musicvis.RenderScriptScene;
+
+import android.media.MediaPlayer;
+import android.os.Handler;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Primitive;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.renderscript.Sampler;
+import android.renderscript.ScriptC;
+import android.renderscript.SimpleMesh;
+import android.renderscript.Type;
+import android.renderscript.Element.Builder;
+import android.renderscript.ProgramStore.BlendDstFunc;
+import android.renderscript.ProgramStore.BlendSrcFunc;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import java.util.TimeZone;
+
+class Visualization5RS extends RenderScriptScene {
+
+    private final Handler mHandler = new Handler();
+    private final Runnable mDrawCube = new Runnable() {
+        public void run() {
+            updateWave();
+        }
+    };
+    private boolean mVisible;
+
+    private int mNeedlePos = 0;
+    private int mNeedleSpeed = 0;
+    // tweak this to get quicker/slower response
+    private int mNeedleMass = 10;
+    private int mSpringForceAtOrigin = 200;
+    
+    static class WorldState {
+        public float mAngle;
+        public int   mPeak;
+        public float mRotate;
+        public float mTilt;
+        public int   mIdle;
+        public int   mWaveCounter;
+    }
+    WorldState mWorldState = new WorldState();
+    private Type mStateType;
+    private Allocation mState;
+
+    private ProgramStore mPfsBackground;
+    private ProgramFragment mPfBackground;
+    private Sampler mSampler;
+    private Allocation[] mTextures;
+    
+    private ProgramVertex mPVBackground;
+    private ProgramVertex.MatrixAllocation mPVAlloc;
+
+    private SimpleMesh mCubeMesh;
+
+    protected Allocation mPointAlloc;
+    // 256 lines, with 4 points per line (2 space, 2 texture) each consisting of x and y,
+    // so 8 floats per line.
+    protected float [] mPointData = new float[256*8];
+
+    private Allocation mLineIdxAlloc;
+    // 2 indices per line
+    private short [] mIndexData = new short[256*2];
+    
+    private short [] mVizData = new short[1024];
+
+    private static final int RSID_STATE = 0;
+    private static final int RSID_POINTS = 1;
+    private static final int RSID_LINES = 2;
+    private static final int RSID_PROGRAMVERTEX = 3;
+
+    private float mTouchY;
+    
+    Visualization5RS(int width, int height) {
+        super(width, height);
+        mWidth = width;
+        mHeight = height;
+        // the x, s and t coordinates don't change, so set those now
+        int outlen = mPointData.length / 8;
+        int half = outlen / 2;
+        for(int i = 0; i < outlen; i++) {
+            mPointData[i*8]   = i - half;          // start point X (Y set later)
+            mPointData[i*8+2] = 0;                 // start point S
+            mPointData[i*8+3] = 0;                 // start point T
+            mPointData[i*8+4] = i - half;          // end point X (Y set later)
+            mPointData[i*8+6] = 1.0f;              // end point S
+            mPointData[i*8+7] = 0f;                // end point T
+        }
+    }
+
+    @Override
+    public void resize(int width, int height) {
+        super.resize(width, height);
+        if (mPVAlloc != null) {
+            mPVAlloc.setupProjectionNormalized(width, height);
+        }
+        mWorldState.mTilt = -20;
+    }
+
+    @Override
+    public void onTouchEvent(MotionEvent event) {
+        switch(event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mTouchY = event.getY();
+                break;
+            case MotionEvent.ACTION_MOVE:
+                float dy = event.getY() - mTouchY;
+                mTouchY += dy;
+                dy /= 10;
+                dy += mWorldState.mTilt;
+                if (dy > 0) {
+                    dy = 0;
+                } else if (dy < -45) {
+                    dy = -45;
+                }
+                mWorldState.mTilt = dy;
+                mState.data(mWorldState);
+        }
+    }
+    
+    @Override
+    public void setOffset(float xOffset, float yOffset, int xPixels, int yPixels) {
+        // update our state, then push it to the renderscript
+        mWorldState.mRotate = (xOffset - 0.5f) * 90;
+        mState.data(mWorldState);
+    }
+
+    @Override
+    protected ScriptC createScript() {
+
+        // Create a renderscript type from a java class. The specified name doesn't
+        // really matter; the name by which we refer to the object in RenderScript
+        // will be specified later.
+        mStateType = Type.createFromClass(mRS, WorldState.class, 1, "WorldState");
+        // Create an allocation from the type we just created.
+        mState = Allocation.createTyped(mRS, mStateType);
+
+        // First set up the coordinate system and such
+        ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null);
+        mPVBackground = pvb.create();
+        mPVBackground.setName("PVBackground");
+        mPVAlloc = new ProgramVertex.MatrixAllocation(mRS);
+        mPVBackground.bindAllocation(mPVAlloc);
+        mPVAlloc.setupProjectionNormalized(mWidth, mHeight);
+
+        mTextures = new Allocation[8];
+        mTextures[0] = Allocation.createFromBitmapResourceBoxed(mRS, mResources, R.drawable.background, Element.RGBA_8888(mRS), false);
+        mTextures[0].setName("Tvumeter_background");
+        mTextures[1] = Allocation.createFromBitmapResourceBoxed(mRS, mResources, R.drawable.frame, Element.RGBA_8888(mRS), false);
+        mTextures[1].setName("Tvumeter_frame");
+        mTextures[2] = Allocation.createFromBitmapResourceBoxed(mRS, mResources, R.drawable.peak_on, Element.RGBA_8888(mRS), false);
+        mTextures[2].setName("Tvumeter_peak_on");
+        mTextures[3] = Allocation.createFromBitmapResourceBoxed(mRS, mResources, R.drawable.peak_off, Element.RGBA_8888(mRS), false);
+        mTextures[3].setName("Tvumeter_peak_off");
+        mTextures[4] = Allocation.createFromBitmapResourceBoxed(mRS, mResources, R.drawable.needle, Element.RGBA_8888(mRS), false);
+        mTextures[4].setName("Tvumeter_needle");
+        mTextures[5] = Allocation.createFromBitmapResourceBoxed(mRS, mResources, R.drawable.black, Element.RGB_565(mRS), false);
+        mTextures[5].setName("Tvumeter_black");
+        mTextures[6] = Allocation.createFromBitmapResource(mRS, mResources, R.drawable.albumart, Element.RGBA_8888(mRS), false);
+        mTextures[6].setName("Tvumeter_album");
+        mTextures[7] = Allocation.createFromBitmapResource(mRS, mResources, R.drawable.fire, Element.RGB_565(mRS), false);
+        mTextures[7].setName("Tlinetexture");
+
+        final int count = mTextures.length;
+        for (int i = 0; i < count; i++) {
+            mTextures[i].uploadToTexture(0);
+        }
+        
+        Sampler.Builder samplerBuilder = new Sampler.Builder(mRS);
+        samplerBuilder.setMin(LINEAR);
+        samplerBuilder.setMag(LINEAR);
+        samplerBuilder.setWrapS(WRAP);
+        samplerBuilder.setWrapT(WRAP);
+        mSampler = samplerBuilder.create();
+
+        {
+            ProgramFragment.Builder builder = new ProgramFragment.Builder(mRS, null, null);
+            builder.setTexEnable(true, 0);
+            builder.setTexEnvMode(REPLACE, 0);
+            mPfBackground = builder.create();
+            mPfBackground.setName("PFBackground");
+            mPfBackground.bindSampler(mSampler, 0);
+        }
+
+        {
+            ProgramStore.Builder builder = new ProgramStore.Builder(mRS, null, null);
+            builder.setDepthFunc(ProgramStore.DepthFunc.EQUAL);
+            //builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+            builder.setDitherEnable(true); // without dithering there is severe banding
+            builder.setDepthMask(false);
+            mPfsBackground = builder.create();
+            mPfsBackground.setName("PFSBackground");
+        }
+        
+        // Start creating the mesh
+        final SimpleMesh.Builder meshBuilder = new SimpleMesh.Builder(mRS);
+
+        // Create the Element for the points
+        Builder elementBuilder = new Builder(mRS);
+        // By specifying a prefix, even an empty one, the members will be accessible
+        // in the renderscript. If we just called addFloatXYZ(), the members would be
+        // unnamed in the renderscript struct definition.
+        elementBuilder.addFloatXY("");
+        elementBuilder.addFloatST("");
+        final Element vertexElement = elementBuilder.create();
+        final int vertexSlot = meshBuilder.addVertexType(vertexElement, mPointData.length / 4);
+        // Specify the type and number of indices we need. We'll allocate them later.
+        meshBuilder.setIndexType(Element.INDEX_16(mRS), mIndexData.length);
+        // This will be a line mesh
+        meshBuilder.setPrimitive(Primitive.LINE);
+
+        // Create the Allocation for the vertices
+        mCubeMesh = meshBuilder.create();
+        mCubeMesh.setName("CubeMesh");
+        mPointAlloc = mCubeMesh.createVertexAllocation(vertexSlot);
+        mPointAlloc.setName("PointBuffer");
+
+        // Create the Allocation for the indices
+        mLineIdxAlloc = mCubeMesh.createIndexAllocation();
+
+        // Bind the allocations to the mesh
+        mCubeMesh.bindVertexAllocation(mPointAlloc, 0);
+        mCubeMesh.bindIndexAllocation(mLineIdxAlloc);
+
+        /*
+         *  put the vertex and index data in their respective buffers
+         */
+        updateWave();
+        for(int i = 0; i < mIndexData.length; i ++) {
+            mIndexData[i] = (short) i;
+        }
+
+        /*
+         *  upload the vertex and index data
+         */
+        mPointAlloc.data(mPointData);
+        mPointAlloc.uploadToBufferObject();
+        mLineIdxAlloc.data(mIndexData);
+        mLineIdxAlloc.uploadToBufferObject();
+
+        // Time to create the script
+        ScriptC.Builder sb = new ScriptC.Builder(mRS);
+        // Specify the name by which to refer to the WorldState object in the
+        // renderscript.
+        sb.setType(mStateType, "State", RSID_STATE);
+        sb.setScript(mResources, R.raw.many);
+        sb.setRoot(true);
+
+        ScriptC script = sb.create();
+        script.setClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+        script.setTimeZone(TimeZone.getDefault().getID());
+
+        script.bindAllocation(mState, RSID_STATE);
+        script.bindAllocation(mPointAlloc, RSID_POINTS);
+        script.bindAllocation(mLineIdxAlloc, RSID_LINES);
+        script.bindAllocation(mPVAlloc.mAlloc, RSID_PROGRAMVERTEX);
+
+        return script;
+    }
+
+    @Override
+    public void start() {
+        super.start();
+        mVisible = true;
+        updateWave();
+    }
+
+    @Override
+    public void stop() {
+        super.stop();
+        mVisible = false;
+    }
+
+    void updateWave() {
+        mHandler.removeCallbacks(mDrawCube);
+        if (mVisible) {
+            mHandler.postDelayed(mDrawCube, 20);
+        }
+
+        int len = MediaPlayer.snoop(mVizData, 0);
+        
+        // Simulate running the signal through a rectifier by
+        // taking the average of the absolute sample values.
+        int volt = 0;
+        if (len > 0) {
+            for (int i = 0; i < len; i++) {
+                int val = mVizData[i];
+                if (val < 0) {
+                    val = -val;
+                }
+                volt += val;
+            }
+            volt = volt * 4 / len; // arbitrary scalar to get better range
+        }
+
+        // There are several forces working on the needle: a force applied by the
+        // electromagnet, a force applied by the spring,  and friction.
+        // The force from the magnet is proportional to the current flowing
+        // through its coil. We have to take in to account that the coil is an
+        // inductive load, which means that an immediate change in applied voltage
+        // will result in a gradual change in current, but also that current will
+        // be induced by the movement of the needle.
+        // The force from the spring is proportional to the position of the needle.
+        // The friction force is a function of the speed of the needle, but so is
+        // the current induced by the movement of the needle, so we can combine
+        // them.
+        
+        
+        // Add up the various forces, with some multipliers to make the movement
+        // of the needle more realistic
+        // 'volt' is for the applied voltage, which causes a current to flow through the coil
+        // mNeedleSpeed * 3 is for the movement of the needle, which induces an opposite current
+        // in the coil, and is also proportional to the friction
+        // mNeedlePos + mSpringForceAtOrigin is for the force of the spring pushing the needle back
+        int netforce = volt - mNeedleSpeed * 3 - (mNeedlePos + mSpringForceAtOrigin) ;
+        int acceleration = netforce / mNeedleMass;
+        mNeedleSpeed += acceleration;
+        mNeedlePos += mNeedleSpeed;
+        if (mNeedlePos < 0) {
+            mNeedlePos = 0;
+            mNeedleSpeed = 0;
+        } else if (mNeedlePos > 32767) {
+            if (mNeedlePos > 33333) {
+                 mWorldState.mPeak = 10;
+            }
+            mNeedlePos = 32767;
+            mNeedleSpeed = 0;
+        }
+        if (mWorldState.mPeak > 0) {
+            mWorldState.mPeak--;
+        }
+
+        mWorldState.mAngle = 131f - (mNeedlePos / 410f); // ~80 degree range
+        
+        // downsample 1024 samples in to 256
+        
+        if (len == 0) {
+            if (mWorldState.mIdle == 0) {
+                mWorldState.mIdle = 1;
+            }
+        } else {
+            if (mWorldState.mIdle != 0) {
+                mWorldState.mIdle = 0;
+            }
+            // TODO: might be more efficient to push this in to renderscript
+            int outlen = mPointData.length / 8;
+            len /= 4;
+            if (len > outlen) len = outlen;
+            for(int i = 0; i < len; i++) {
+                int amp = (mVizData[i*4]  + mVizData[i*4+1] + mVizData[i*4+2] + mVizData[i*4+3]);
+                mPointData[i*8+1] = amp;
+                mPointData[i*8+5] = -amp;
+            }
+            mPointAlloc.data(mPointData);
+            mWorldState.mWaveCounter++;
+        }
+        
+        mState.data(mWorldState);
+    }
+
+}