• 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                                            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
start()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
stop()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 
updateWave()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