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                                            Allocation.MipmapControl.MIPMAP_NONE,
    113                                            Allocation.USAGE_GRAPHICS_TEXTURE);
    114         mScript.set_gTvumeter_background(mTextures[0]);
    115         mTextures[1] = Allocation.createFromBitmapResource(mRS, mResources, R.drawable.frame,
    116                                            Allocation.MipmapControl.MIPMAP_NONE,
    117                                            Allocation.USAGE_GRAPHICS_TEXTURE);
    118         mScript.set_gTvumeter_frame(mTextures[1]);
    119         mTextures[2] = Allocation.createFromBitmapResource(mRS, mResources, R.drawable.peak_on,
    120                                            Allocation.MipmapControl.MIPMAP_NONE,
    121                                            Allocation.USAGE_GRAPHICS_TEXTURE);
    122         mScript.set_gTvumeter_peak_on(mTextures[2]);
    123         mTextures[3] = Allocation.createFromBitmapResource(mRS, mResources, R.drawable.peak_off,
    124                                            Allocation.MipmapControl.MIPMAP_NONE,
    125                                            Allocation.USAGE_GRAPHICS_TEXTURE);
    126         mScript.set_gTvumeter_peak_off(mTextures[3]);
    127         mTextures[4] = Allocation.createFromBitmapResource(mRS, mResources, R.drawable.needle,
    128                                            Allocation.MipmapControl.MIPMAP_NONE,
    129                                            Allocation.USAGE_GRAPHICS_TEXTURE);
    130         mScript.set_gTvumeter_needle(mTextures[4]);
    131         mTextures[5] = Allocation.createFromBitmapResource(mRS, mResources, R.drawable.black,
    132                                            Allocation.MipmapControl.MIPMAP_NONE,
    133                                            Allocation.USAGE_GRAPHICS_TEXTURE);
    134         mScript.set_gTvumeter_black(mTextures[5]);
    135 
    136         Sampler.Builder samplerBuilder = new Sampler.Builder(mRS);
    137         samplerBuilder.setMinification(LINEAR);
    138         samplerBuilder.setMagnification(LINEAR);
    139         samplerBuilder.setWrapS(WRAP);
    140         samplerBuilder.setWrapT(WRAP);
    141         mSampler = samplerBuilder.create();
    142 
    143         {
    144             ProgramFragmentFixedFunction.Builder builder = new ProgramFragmentFixedFunction.Builder(mRS);
    145             builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
    146                                ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
    147             mPfBackground = builder.create();
    148             mPfBackground.bindSampler(mSampler, 0);
    149 
    150             mScript.set_gPFBackground(mPfBackground);
    151         }
    152 
    153         {
    154             ProgramStore.Builder builder = new ProgramStore.Builder(mRS);
    155             builder.setDepthFunc(ALWAYS);
    156             //builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
    157             builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
    158             builder.setDitherEnabled(true); // without dithering there is severe banding
    159             builder.setDepthMaskEnabled(false);
    160             mPfsBackground = builder.create();
    161 
    162             mScript.set_gPFSBackground(mPfsBackground);
    163         }
    164 
    165         mScript.setTimeZone(TimeZone.getDefault().getID());
    166 
    167         return mScript;
    168     }
    169 
    170     @Override
    171     public void start() {
    172         super.start();
    173         mVisible = true;
    174         if (mAudioCapture == null) {
    175             mAudioCapture = new AudioCapture(AudioCapture.TYPE_PCM, 1024);
    176         }
    177         mAudioCapture.start();
    178         updateWave();
    179     }
    180 
    181     @Override
    182     public void stop() {
    183         super.stop();
    184         mVisible = false;
    185         if (mAudioCapture != null) {
    186             mAudioCapture.stop();
    187             mAudioCapture.release();
    188             mAudioCapture = null;
    189         }
    190     }
    191 
    192     void updateWave() {
    193         mHandler.removeCallbacks(mDrawCube);
    194         if (!mVisible) {
    195             return;
    196         }
    197         mHandler.postDelayed(mDrawCube, 20);
    198 
    199         int len = 0;
    200         if (mAudioCapture != null) {
    201             // arbitrary scalar to get better range: 512 = 2 * 256 (256 for 8 to 16 bit)
    202             mVizData = mAudioCapture.getFormattedData(512, 1);
    203             len = mVizData.length;
    204         }
    205 
    206         // Simulate running the signal through a rectifier by
    207         // taking the average of the absolute sample values.
    208         int volt = 0;
    209         if (len > 0) {
    210             for (int i = 0; i < len; i++) {
    211                 int val = mVizData[i];
    212                 if (val < 0) {
    213                     val = -val;
    214                 }
    215                 volt += val;
    216             }
    217             volt = volt / len;
    218         }
    219         // There are several forces working on the needle: a force applied by the
    220         // electromagnet, a force applied by the spring,  and friction.
    221         // The force from the magnet is proportional to the current flowing
    222         // through its coil. We have to take in to account that the coil is an
    223         // inductive load, which means that an immediate change in applied voltage
    224         // will result in a gradual change in current, but also that current will
    225         // be induced by the movement of the needle.
    226         // The force from the spring is proportional to the position of the needle.
    227         // The friction force is a function of the speed of the needle, but so is
    228         // the current induced by the movement of the needle, so we can combine
    229         // them.
    230 
    231 
    232         // Add up the various forces, with some multipliers to make the movement
    233         // of the needle more realistic
    234         // 'volt' is for the applied voltage, which causes a current to flow through the coil
    235         // mNeedleSpeed * 3 is for the movement of the needle, which induces an opposite current
    236         // in the coil, and is also proportional to the friction
    237         // mNeedlePos + mSpringForceAtOrigin is for the force of the spring pushing the needle back
    238         int netforce = volt - mNeedleSpeed * 3 - (mNeedlePos + mSpringForceAtOrigin) ;
    239         int acceleration = netforce / mNeedleMass;
    240         mNeedleSpeed += acceleration;
    241         mNeedlePos += mNeedleSpeed;
    242         if (mNeedlePos < 0) {
    243             mNeedlePos = 0;
    244             mNeedleSpeed = 0;
    245         } else if (mNeedlePos > 32767) {
    246             if (mNeedlePos > 33333) {
    247                  mWorldState.mPeak = 10;
    248             }
    249             mNeedlePos = 32767;
    250             mNeedleSpeed = 0;
    251         }
    252         if (mWorldState.mPeak > 0) {
    253             mWorldState.mPeak--;
    254         }
    255 
    256         mWorldState.mAngle = 131f - (mNeedlePos / 410f); // ~80 degree range
    257         mScript.set_gAngle(mWorldState.mAngle);
    258         mScript.set_gPeak(mWorldState.mPeak);
    259     }
    260 }
    261