Home | History | Annotate | Download | only in vis4
      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.vis4;
     18 
     19 import static android.renderscript.ProgramStore.DepthFunc.ALWAYS;
     20 import static android.renderscript.Sampler.Value.LINEAR;
     21 import static android.renderscript.Sampler.Value.WRAP;
     22 
     23 import com.android.musicvis.R;
     24 import com.android.musicvis.RenderScriptScene;
     25 import com.android.musicvis.AudioCapture;
     26 
     27 import android.os.Handler;
     28 import android.renderscript.*;
     29 import android.renderscript.ProgramStore.BlendDstFunc;
     30 import android.renderscript.ProgramStore.BlendSrcFunc;
     31 
     32 import java.util.TimeZone;
     33 import android.util.Log;
     34 
     35 class Visualization4RS extends RenderScriptScene {
     36 
     37     private final Handler mHandler = new Handler();
     38     private final Runnable mDrawCube = new Runnable() {
     39         public void run() {
     40             updateWave();
     41         }
     42     };
     43     private boolean mVisible;
     44 
     45     private int mNeedlePos = 0;
     46     private int mNeedleSpeed = 0;
     47     // tweak this to get quicker/slower response
     48     private int mNeedleMass = 10;
     49     private int mSpringForceAtOrigin = 200;
     50 
     51     static class WorldState {
     52         public float mAngle;
     53         public int   mPeak;
     54     }
     55     WorldState mWorldState = new WorldState();
     56 
     57     ScriptC_vu mScript;
     58 
     59     private ProgramStore mPfsBackground;
     60     private ProgramFragment mPfBackground;
     61     private Sampler mSampler;
     62     private Allocation[] mTextures;
     63 
     64     private ProgramVertex mPVBackground;
     65     private ProgramVertexFixedFunction.Constants mPVAlloc;
     66 
     67     private AudioCapture mAudioCapture = null;
     68     private int [] mVizData = new int[1024];
     69 
     70     private static final int RSID_STATE = 0;
     71     private static final int RSID_POINTS = 1;
     72     private static final int RSID_LINES = 2;
     73     private static final int RSID_PROGRAMVERTEX = 3;
     74 
     75 
     76     Visualization4RS(int width, int height) {
     77         super(width, height);
     78         mWidth = width;
     79         mHeight = height;
     80     }
     81 
     82     @Override
     83     public void resize(int width, int height) {
     84         super.resize(width, height);
     85         if (mPVAlloc != null) {
     86             Matrix4f proj = new Matrix4f();
     87             proj.loadProjectionNormalized(width, height);
     88             mPVAlloc.setProjection(proj);
     89         }
     90     }
     91 
     92     @Override
     93     protected ScriptC createScript() {
     94 
     95         mScript = new ScriptC_vu(mRS, mResources, R.raw.vu);
     96 
     97         // First set up the coordinate system and such
     98         ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
     99         mPVBackground = pvb.create();
    100         mPVAlloc = new ProgramVertexFixedFunction.Constants(mRS);
    101         ((ProgramVertexFixedFunction)mPVBackground).bindConstants(mPVAlloc);
    102         Matrix4f proj = new Matrix4f();
    103         proj.loadProjectionNormalized(mWidth, mHeight);
    104         mPVAlloc.setProjection(proj);
    105 
    106         mScript.set_gPVBackground(mPVBackground);
    107 
    108         updateWave();
    109 
    110         mTextures = new Allocation[6];
    111         mTextures[0] = Allocation.createFromBitmapResource(mRS, mResources, R.drawable.background);
    112         mScript.set_gTvumeter_background(mTextures[0]);
    113         mTextures[1] = Allocation.createFromBitmapResource(mRS, mResources, R.drawable.frame);
    114         mScript.set_gTvumeter_frame(mTextures[1]);
    115         mTextures[2] = Allocation.createFromBitmapResource(mRS, mResources, R.drawable.peak_on);
    116         mScript.set_gTvumeter_peak_on(mTextures[2]);
    117         mTextures[3] = Allocation.createFromBitmapResource(mRS, mResources, R.drawable.peak_off);
    118         mScript.set_gTvumeter_peak_off(mTextures[3]);
    119         mTextures[4] = Allocation.createFromBitmapResource(mRS, mResources, R.drawable.needle);
    120         mScript.set_gTvumeter_needle(mTextures[4]);
    121         mTextures[5] = Allocation.createFromBitmapResource(mRS, mResources, R.drawable.black);
    122         mScript.set_gTvumeter_black(mTextures[5]);
    123 
    124         Sampler.Builder samplerBuilder = new Sampler.Builder(mRS);
    125         samplerBuilder.setMinification(LINEAR);
    126         samplerBuilder.setMagnification(LINEAR);
    127         samplerBuilder.setWrapS(WRAP);
    128         samplerBuilder.setWrapT(WRAP);
    129         mSampler = samplerBuilder.create();
    130 
    131         {
    132             ProgramFragmentFixedFunction.Builder builder = new ProgramFragmentFixedFunction.Builder(mRS);
    133             builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
    134                                ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
    135             mPfBackground = builder.create();
    136             mPfBackground.bindSampler(mSampler, 0);
    137 
    138             mScript.set_gPFBackground(mPfBackground);
    139         }
    140 
    141         {
    142             ProgramStore.Builder builder = new ProgramStore.Builder(mRS);
    143             builder.setDepthFunc(ALWAYS);
    144             //builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
    145             builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
    146             builder.setDitherEnabled(true); // without dithering there is severe banding
    147             builder.setDepthMaskEnabled(false);
    148             mPfsBackground = builder.create();
    149 
    150             mScript.set_gPFSBackground(mPfsBackground);
    151         }
    152 
    153         mScript.setTimeZone(TimeZone.getDefault().getID());
    154 
    155         return mScript;
    156     }
    157 
    158     @Override
    159     public void start() {
    160         super.start();
    161         mVisible = true;
    162         if (mAudioCapture == null) {
    163             mAudioCapture = new AudioCapture(AudioCapture.TYPE_PCM, 1024);
    164         }
    165         mAudioCapture.start();
    166         updateWave();
    167     }
    168 
    169     @Override
    170     public void stop() {
    171         super.stop();
    172         mVisible = false;
    173         if (mAudioCapture != null) {
    174             mAudioCapture.stop();
    175             mAudioCapture.release();
    176             mAudioCapture = null;
    177         }
    178     }
    179 
    180     void updateWave() {
    181         mHandler.removeCallbacks(mDrawCube);
    182         if (!mVisible) {
    183             return;
    184         }
    185         mHandler.postDelayed(mDrawCube, 20);
    186 
    187         int len = 0;
    188         if (mAudioCapture != null) {
    189             // arbitrary scalar to get better range: 512 = 2 * 256 (256 for 8 to 16 bit)
    190             mVizData = mAudioCapture.getFormattedData(512, 1);
    191             len = mVizData.length;
    192         }
    193 
    194         // Simulate running the signal through a rectifier by
    195         // taking the average of the absolute sample values.
    196         int volt = 0;
    197         if (len > 0) {
    198             for (int i = 0; i < len; i++) {
    199                 int val = mVizData[i];
    200                 if (val < 0) {
    201                     val = -val;
    202                 }
    203                 volt += val;
    204             }
    205             volt = volt / len;
    206         }
    207         // There are several forces working on the needle: a force applied by the
    208         // electromagnet, a force applied by the spring,  and friction.
    209         // The force from the magnet is proportional to the current flowing
    210         // through its coil. We have to take in to account that the coil is an
    211         // inductive load, which means that an immediate change in applied voltage
    212         // will result in a gradual change in current, but also that current will
    213         // be induced by the movement of the needle.
    214         // The force from the spring is proportional to the position of the needle.
    215         // The friction force is a function of the speed of the needle, but so is
    216         // the current induced by the movement of the needle, so we can combine
    217         // them.
    218 
    219 
    220         // Add up the various forces, with some multipliers to make the movement
    221         // of the needle more realistic
    222         // 'volt' is for the applied voltage, which causes a current to flow through the coil
    223         // mNeedleSpeed * 3 is for the movement of the needle, which induces an opposite current
    224         // in the coil, and is also proportional to the friction
    225         // mNeedlePos + mSpringForceAtOrigin is for the force of the spring pushing the needle back
    226         int netforce = volt - mNeedleSpeed * 3 - (mNeedlePos + mSpringForceAtOrigin) ;
    227         int acceleration = netforce / mNeedleMass;
    228         mNeedleSpeed += acceleration;
    229         mNeedlePos += mNeedleSpeed;
    230         if (mNeedlePos < 0) {
    231             mNeedlePos = 0;
    232             mNeedleSpeed = 0;
    233         } else if (mNeedlePos > 32767) {
    234             if (mNeedlePos > 33333) {
    235                  mWorldState.mPeak = 10;
    236             }
    237             mNeedlePos = 32767;
    238             mNeedleSpeed = 0;
    239         }
    240         if (mWorldState.mPeak > 0) {
    241             mWorldState.mPeak--;
    242         }
    243 
    244         mWorldState.mAngle = 131f - (mNeedlePos / 410f); // ~80 degree range
    245         mScript.set_gAngle(mWorldState.mAngle);
    246         mScript.set_gPeak(mWorldState.mPeak);
    247     }
    248 }
    249