• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Visualization4RS(int width, int height)76     Visualization4RS(int width, int height) {
77         super(width, height);
78         mWidth = width;
79         mHeight = height;
80     }
81 
82     @Override
resize(int width, int height)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
createScript()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
start()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
stop()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 
updateWave()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