blob: b4796f351afd555da27787de7b4f4e0256845679 [file] [log] [blame]
Marco Nelissen838dedb2009-11-03 17:01:21 -08001/*
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
17package com.android.musicvis.vis5;
18
Marco Nelissen838dedb2009-11-03 17:01:21 -080019import com.android.musicvis.R;
20import com.android.musicvis.RenderScriptScene;
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -070021import com.android.musicvis.ScriptField_Vertex;
Eric Laurentf6b1bdc2010-07-04 16:06:26 -070022import com.android.musicvis.AudioCapture;
Marco Nelissen838dedb2009-11-03 17:01:21 -080023
Marco Nelissen838dedb2009-11-03 17:01:21 -080024import android.os.Handler;
25import android.renderscript.Allocation;
26import android.renderscript.Element;
27import android.renderscript.Primitive;
28import android.renderscript.ProgramFragment;
29import android.renderscript.ProgramStore;
30import android.renderscript.ProgramVertex;
31import android.renderscript.Sampler;
32import android.renderscript.ScriptC;
Alex Sakhartchoukaf04b9b2010-07-01 17:20:14 -070033import android.renderscript.Mesh;
Marco Nelissen838dedb2009-11-03 17:01:21 -080034import android.renderscript.Type;
35import android.renderscript.Element.Builder;
36import android.renderscript.ProgramStore.BlendDstFunc;
37import android.renderscript.ProgramStore.BlendSrcFunc;
Marco Nelissen24c44572009-11-20 11:30:32 -080038import android.renderscript.Sampler.Value;
Marco Nelissen838dedb2009-11-03 17:01:21 -080039import android.util.Log;
40import android.view.MotionEvent;
41
42import java.util.TimeZone;
43
44class Visualization5RS extends RenderScriptScene {
45
46 private final Handler mHandler = new Handler();
47 private final Runnable mDrawCube = new Runnable() {
48 public void run() {
49 updateWave();
50 }
51 };
52 private boolean mVisible;
53
54 private int mNeedlePos = 0;
55 private int mNeedleSpeed = 0;
56 // tweak this to get quicker/slower response
57 private int mNeedleMass = 10;
58 private int mSpringForceAtOrigin = 200;
Jason Sams27b89e72009-12-17 18:46:45 -080059
Marco Nelissen838dedb2009-11-03 17:01:21 -080060 static class WorldState {
61 public float mAngle;
62 public int mPeak;
63 public float mRotate;
64 public float mTilt;
65 public int mIdle;
66 public int mWaveCounter;
67 }
68 WorldState mWorldState = new WorldState();
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -070069
Stephen Hinese04dd5b2010-09-16 17:22:24 -070070 ScriptC_many mScript;
Shih-wei Liao359640d2010-06-18 06:57:45 -070071 private com.android.musicvis.vis5.ScriptField_Vertex mVertexBuffer;
Marco Nelissen838dedb2009-11-03 17:01:21 -080072
73 private ProgramStore mPfsBackground;
Marco Nelissen24c44572009-11-20 11:30:32 -080074 private ProgramFragment mPfBackgroundMip;
75 private ProgramFragment mPfBackgroundNoMip;
76 private Sampler mSamplerMip;
77 private Sampler mSamplerNoMip;
Marco Nelissen838dedb2009-11-03 17:01:21 -080078 private Allocation[] mTextures;
Jason Sams27b89e72009-12-17 18:46:45 -080079
Marco Nelissen838dedb2009-11-03 17:01:21 -080080 private ProgramVertex mPVBackground;
81 private ProgramVertex.MatrixAllocation mPVAlloc;
82
Alex Sakhartchoukaf04b9b2010-07-01 17:20:14 -070083 private Mesh mCubeMesh;
Marco Nelissen838dedb2009-11-03 17:01:21 -080084
85 protected Allocation mPointAlloc;
86 // 256 lines, with 4 points per line (2 space, 2 texture) each consisting of x and y,
87 // so 8 floats per line.
88 protected float [] mPointData = new float[256*8];
89
90 private Allocation mLineIdxAlloc;
91 // 2 indices per line
92 private short [] mIndexData = new short[256*2];
Jason Sams27b89e72009-12-17 18:46:45 -080093
Eric Laurentf6b1bdc2010-07-04 16:06:26 -070094 private AudioCapture mAudioCapture = null;
95 private int [] mVizData = new int[1024];
Marco Nelissen838dedb2009-11-03 17:01:21 -080096
97 private static final int RSID_STATE = 0;
98 private static final int RSID_POINTS = 1;
99 private static final int RSID_LINES = 2;
100 private static final int RSID_PROGRAMVERTEX = 3;
101
102 private float mTouchY;
Jason Sams27b89e72009-12-17 18:46:45 -0800103
Marco Nelissen838dedb2009-11-03 17:01:21 -0800104 Visualization5RS(int width, int height) {
105 super(width, height);
106 mWidth = width;
107 mHeight = height;
108 // the x, s and t coordinates don't change, so set those now
109 int outlen = mPointData.length / 8;
110 int half = outlen / 2;
111 for(int i = 0; i < outlen; i++) {
112 mPointData[i*8] = i - half; // start point X (Y set later)
113 mPointData[i*8+2] = 0; // start point S
114 mPointData[i*8+3] = 0; // start point T
115 mPointData[i*8+4] = i - half; // end point X (Y set later)
116 mPointData[i*8+6] = 1.0f; // end point S
117 mPointData[i*8+7] = 0f; // end point T
118 }
119 }
120
121 @Override
122 public void resize(int width, int height) {
123 super.resize(width, height);
124 if (mPVAlloc != null) {
125 mPVAlloc.setupProjectionNormalized(width, height);
126 }
127 mWorldState.mTilt = -20;
128 }
129
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700130 /*@Override
Marco Nelissen838dedb2009-11-03 17:01:21 -0800131 public void onTouchEvent(MotionEvent event) {
132 switch(event.getAction()) {
133 case MotionEvent.ACTION_DOWN:
134 mTouchY = event.getY();
135 break;
136 case MotionEvent.ACTION_MOVE:
137 float dy = event.getY() - mTouchY;
138 mTouchY += dy;
139 dy /= 10;
140 dy += mWorldState.mTilt;
141 if (dy > 0) {
142 dy = 0;
143 } else if (dy < -45) {
144 dy = -45;
145 }
146 mWorldState.mTilt = dy;
147 mState.data(mWorldState);
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700148 //updateWorldState();
Marco Nelissen838dedb2009-11-03 17:01:21 -0800149 }
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700150 }*/
Jason Sams27b89e72009-12-17 18:46:45 -0800151
Marco Nelissen838dedb2009-11-03 17:01:21 -0800152 @Override
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700153 public void setOffset(float xOffset, float yOffset, int xPixels, int yPixels) {
Marco Nelissen838dedb2009-11-03 17:01:21 -0800154 // update our state, then push it to the renderscript
155 mWorldState.mRotate = (xOffset - 0.5f) * 90;
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700156 updateWorldState();
Marco Nelissen838dedb2009-11-03 17:01:21 -0800157 }
158
159 @Override
160 protected ScriptC createScript() {
Stephen Hinese04dd5b2010-09-16 17:22:24 -0700161 mScript = new ScriptC_many(mRS, mResources, R.raw.many, true);
Marco Nelissen838dedb2009-11-03 17:01:21 -0800162
163 // First set up the coordinate system and such
164 ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null);
165 mPVBackground = pvb.create();
Marco Nelissen838dedb2009-11-03 17:01:21 -0800166 mPVAlloc = new ProgramVertex.MatrixAllocation(mRS);
167 mPVBackground.bindAllocation(mPVAlloc);
168 mPVAlloc.setupProjectionNormalized(mWidth, mHeight);
169
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700170 mScript.set_gPVBackground(mPVBackground);
171
Marco Nelissen838dedb2009-11-03 17:01:21 -0800172 mTextures = new Allocation[8];
Marco Nelissen24c44572009-11-20 11:30:32 -0800173 mTextures[0] = Allocation.createFromBitmapResourceBoxed(mRS, mResources, R.drawable.background, Element.RGBA_8888(mRS), true);
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700174 mScript.set_gTvumeter_background(mTextures[0]);
Marco Nelissen24c44572009-11-20 11:30:32 -0800175 mTextures[1] = Allocation.createFromBitmapResourceBoxed(mRS, mResources, R.drawable.frame, Element.RGBA_8888(mRS), true);
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700176 mScript.set_gTvumeter_frame(mTextures[1]);
Marco Nelissen24c44572009-11-20 11:30:32 -0800177 mTextures[2] = Allocation.createFromBitmapResourceBoxed(mRS, mResources, R.drawable.peak_on, Element.RGBA_8888(mRS), true);
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700178 mScript.set_gTvumeter_peak_on(mTextures[2]);
Marco Nelissen24c44572009-11-20 11:30:32 -0800179 mTextures[3] = Allocation.createFromBitmapResourceBoxed(mRS, mResources, R.drawable.peak_off, Element.RGBA_8888(mRS), true);
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700180 mScript.set_gTvumeter_peak_off(mTextures[3]);
Marco Nelissen24c44572009-11-20 11:30:32 -0800181 mTextures[4] = Allocation.createFromBitmapResourceBoxed(mRS, mResources, R.drawable.needle, Element.RGBA_8888(mRS), true);
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700182 mScript.set_gTvumeter_needle(mTextures[4]);
183 mTextures[5] = Allocation.createFromBitmapResourceBoxed(mRS, mResources, R.drawable.black, Element.RGB_565(mRS), true);
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700184 mScript.set_gTvumeter_black(mTextures[5]);
Marco Nelissen24c44572009-11-20 11:30:32 -0800185 mTextures[6] = Allocation.createFromBitmapResource(mRS, mResources, R.drawable.albumart, Element.RGBA_8888(mRS), true);
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700186 mScript.set_gTvumeter_album(mTextures[6]);
Marco Nelissen838dedb2009-11-03 17:01:21 -0800187 mTextures[7] = Allocation.createFromBitmapResource(mRS, mResources, R.drawable.fire, Element.RGB_565(mRS), false);
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700188 mScript.set_gTlinetexture(mTextures[7]);
Marco Nelissen838dedb2009-11-03 17:01:21 -0800189
190 final int count = mTextures.length;
191 for (int i = 0; i < count; i++) {
192 mTextures[i].uploadToTexture(0);
193 }
Marco Nelissen24c44572009-11-20 11:30:32 -0800194
195 {
196 Sampler.Builder builder = new Sampler.Builder(mRS);
197 builder.setMin(Value.LINEAR);
198 builder.setMag(Value.LINEAR);
199 builder.setWrapS(Value.WRAP);
200 builder.setWrapT(Value.WRAP);
201 mSamplerNoMip = builder.create();
202 }
203
204 {
205 Sampler.Builder builder = new Sampler.Builder(mRS);
206 builder.setMin(Value.LINEAR_MIP_LINEAR);
207 builder.setMag(Value.LINEAR);
208 builder.setWrapS(Value.WRAP);
209 builder.setWrapT(Value.WRAP);
210 mSamplerMip = builder.create();
211 }
Marco Nelissen838dedb2009-11-03 17:01:21 -0800212
213 {
Jason Sams27b89e72009-12-17 18:46:45 -0800214 ProgramFragment.Builder builder = new ProgramFragment.Builder(mRS);
215 builder.setTexture(ProgramFragment.Builder.EnvMode.REPLACE,
216 ProgramFragment.Builder.Format.RGBA, 0);
Marco Nelissen24c44572009-11-20 11:30:32 -0800217 mPfBackgroundNoMip = builder.create();
Marco Nelissen24c44572009-11-20 11:30:32 -0800218 mPfBackgroundNoMip.bindSampler(mSamplerNoMip, 0);
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700219 mScript.set_gPFBackgroundNoMip(mPfBackgroundNoMip);
Marco Nelissen24c44572009-11-20 11:30:32 -0800220 }
Jason Sams27b89e72009-12-17 18:46:45 -0800221
Marco Nelissen24c44572009-11-20 11:30:32 -0800222 {
Jason Sams27b89e72009-12-17 18:46:45 -0800223 ProgramFragment.Builder builder = new ProgramFragment.Builder(mRS);
224 builder.setTexture(ProgramFragment.Builder.EnvMode.REPLACE,
225 ProgramFragment.Builder.Format.RGBA, 0);
Marco Nelissen24c44572009-11-20 11:30:32 -0800226 mPfBackgroundMip = builder.create();
Marco Nelissen24c44572009-11-20 11:30:32 -0800227 mPfBackgroundMip.bindSampler(mSamplerMip, 0);
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700228 mScript.set_gPFBackgroundMip(mPfBackgroundMip);
Marco Nelissen838dedb2009-11-03 17:01:21 -0800229 }
230
231 {
232 ProgramStore.Builder builder = new ProgramStore.Builder(mRS, null, null);
233 builder.setDepthFunc(ProgramStore.DepthFunc.EQUAL);
234 //builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
235 builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
236 builder.setDitherEnable(true); // without dithering there is severe banding
237 builder.setDepthMask(false);
238 mPfsBackground = builder.create();
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700239
240 mScript.set_gPFSBackground(mPfsBackground);
Marco Nelissen838dedb2009-11-03 17:01:21 -0800241 }
Jason Sams27b89e72009-12-17 18:46:45 -0800242
Marco Nelissen838dedb2009-11-03 17:01:21 -0800243 // Start creating the mesh
Shih-wei Liao359640d2010-06-18 06:57:45 -0700244 mVertexBuffer = new com.android.musicvis.vis5.ScriptField_Vertex(mRS, mPointData.length / 4);
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700245
Alex Sakhartchoukaf04b9b2010-07-01 17:20:14 -0700246 final Mesh.AllocationBuilder meshBuilder = new Mesh.AllocationBuilder(mRS);
247 meshBuilder.addVertexAllocation(mVertexBuffer.getAllocation());
248 // Create the Allocation for the indices
249 mLineIdxAlloc = Allocation.createSized(mRS, Element.U16(mRS), mIndexData.length);
Marco Nelissen838dedb2009-11-03 17:01:21 -0800250 // This will be a line mesh
Alex Sakhartchoukaf04b9b2010-07-01 17:20:14 -0700251 meshBuilder.addIndexAllocation(mLineIdxAlloc, Primitive.LINE);
Marco Nelissen838dedb2009-11-03 17:01:21 -0800252
253 // Create the Allocation for the vertices
254 mCubeMesh = meshBuilder.create();
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700255
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700256 mPointAlloc = mVertexBuffer.getAllocation();
257
Shih-wei Liao6e3b8112010-06-17 18:06:08 -0700258 mScript.bind_gPoints(mVertexBuffer);
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700259 mScript.set_gPointBuffer(mPointAlloc);
260 mScript.set_gCubeMesh(mCubeMesh);
Marco Nelissen838dedb2009-11-03 17:01:21 -0800261
Jason Samsa811c212010-05-11 14:00:38 -0700262 // put the vertex and index data in their respective buffers
Marco Nelissen838dedb2009-11-03 17:01:21 -0800263 updateWave();
264 for(int i = 0; i < mIndexData.length; i ++) {
265 mIndexData[i] = (short) i;
266 }
267
Jason Samsa811c212010-05-11 14:00:38 -0700268 // upload the vertex and index data
Marco Nelissen838dedb2009-11-03 17:01:21 -0800269 mPointAlloc.data(mPointData);
Marco Nelissen838dedb2009-11-03 17:01:21 -0800270 mLineIdxAlloc.data(mIndexData);
271 mLineIdxAlloc.uploadToBufferObject();
272
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700273 return mScript;
Marco Nelissen838dedb2009-11-03 17:01:21 -0800274 }
275
276 @Override
277 public void start() {
278 super.start();
279 mVisible = true;
Eric Laurentf6b1bdc2010-07-04 16:06:26 -0700280 if (mAudioCapture == null) {
281 mAudioCapture = new AudioCapture(AudioCapture.TYPE_PCM, 1024);
282 }
283 mAudioCapture.start();
Marco Nelissen838dedb2009-11-03 17:01:21 -0800284 updateWave();
285 }
286
287 @Override
288 public void stop() {
289 super.stop();
290 mVisible = false;
Eric Laurentf6b1bdc2010-07-04 16:06:26 -0700291 if (mAudioCapture != null) {
292 mAudioCapture.stop();
293 mAudioCapture.release();
294 mAudioCapture = null;
295 }
Marco Nelissen838dedb2009-11-03 17:01:21 -0800296 }
297
298 void updateWave() {
299 mHandler.removeCallbacks(mDrawCube);
Marco Nelissend760ff32009-11-10 14:49:08 -0800300 if (!mVisible) {
301 return;
Marco Nelissen838dedb2009-11-03 17:01:21 -0800302 }
Marco Nelissend760ff32009-11-10 14:49:08 -0800303 mHandler.postDelayed(mDrawCube, 20);
Marco Nelissen838dedb2009-11-03 17:01:21 -0800304
Eric Laurentf6b1bdc2010-07-04 16:06:26 -0700305 int len = 0;
306 if (mAudioCapture != null) {
307 // arbitrary scalar to get better range: 1024 = 4 * 256 (256 for 8 to 16 bit)
308 mVizData = mAudioCapture.getFormattedData(1024, 1);
309 len = mVizData.length;
310 }
Jason Sams27b89e72009-12-17 18:46:45 -0800311
Marco Nelissen838dedb2009-11-03 17:01:21 -0800312 // Simulate running the signal through a rectifier by
313 // taking the average of the absolute sample values.
314 int volt = 0;
315 if (len > 0) {
316 for (int i = 0; i < len; i++) {
317 int val = mVizData[i];
318 if (val < 0) {
319 val = -val;
320 }
321 volt += val;
322 }
Eric Laurentf6b1bdc2010-07-04 16:06:26 -0700323 volt = volt / len;
Marco Nelissen838dedb2009-11-03 17:01:21 -0800324 }
325
326 // There are several forces working on the needle: a force applied by the
327 // electromagnet, a force applied by the spring, and friction.
328 // The force from the magnet is proportional to the current flowing
329 // through its coil. We have to take in to account that the coil is an
330 // inductive load, which means that an immediate change in applied voltage
331 // will result in a gradual change in current, but also that current will
332 // be induced by the movement of the needle.
333 // The force from the spring is proportional to the position of the needle.
334 // The friction force is a function of the speed of the needle, but so is
335 // the current induced by the movement of the needle, so we can combine
336 // them.
Jason Sams27b89e72009-12-17 18:46:45 -0800337
338
Marco Nelissen838dedb2009-11-03 17:01:21 -0800339 // Add up the various forces, with some multipliers to make the movement
340 // of the needle more realistic
341 // 'volt' is for the applied voltage, which causes a current to flow through the coil
342 // mNeedleSpeed * 3 is for the movement of the needle, which induces an opposite current
343 // in the coil, and is also proportional to the friction
344 // mNeedlePos + mSpringForceAtOrigin is for the force of the spring pushing the needle back
345 int netforce = volt - mNeedleSpeed * 3 - (mNeedlePos + mSpringForceAtOrigin) ;
346 int acceleration = netforce / mNeedleMass;
347 mNeedleSpeed += acceleration;
348 mNeedlePos += mNeedleSpeed;
349 if (mNeedlePos < 0) {
350 mNeedlePos = 0;
351 mNeedleSpeed = 0;
352 } else if (mNeedlePos > 32767) {
353 if (mNeedlePos > 33333) {
354 mWorldState.mPeak = 10;
355 }
356 mNeedlePos = 32767;
357 mNeedleSpeed = 0;
358 }
359 if (mWorldState.mPeak > 0) {
360 mWorldState.mPeak--;
361 }
362
363 mWorldState.mAngle = 131f - (mNeedlePos / 410f); // ~80 degree range
Jason Sams27b89e72009-12-17 18:46:45 -0800364
Marco Nelissen838dedb2009-11-03 17:01:21 -0800365 // downsample 1024 samples in to 256
Jason Sams27b89e72009-12-17 18:46:45 -0800366
Marco Nelissen838dedb2009-11-03 17:01:21 -0800367 if (len == 0) {
368 if (mWorldState.mIdle == 0) {
369 mWorldState.mIdle = 1;
370 }
371 } else {
372 if (mWorldState.mIdle != 0) {
373 mWorldState.mIdle = 0;
374 }
375 // TODO: might be more efficient to push this in to renderscript
376 int outlen = mPointData.length / 8;
377 len /= 4;
378 if (len > outlen) len = outlen;
379 for(int i = 0; i < len; i++) {
380 int amp = (mVizData[i*4] + mVizData[i*4+1] + mVizData[i*4+2] + mVizData[i*4+3]);
381 mPointData[i*8+1] = amp;
382 mPointData[i*8+5] = -amp;
383 }
384 mPointAlloc.data(mPointData);
385 mWorldState.mWaveCounter++;
386 }
Jason Sams27b89e72009-12-17 18:46:45 -0800387
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700388 updateWorldState();
Marco Nelissen838dedb2009-11-03 17:01:21 -0800389 }
390
Alex Sakhartchouk7eb6bcc2010-05-11 14:22:20 -0700391 protected void updateWorldState() {
392 mScript.set_gAngle(mWorldState.mAngle);
393 mScript.set_gPeak(mWorldState.mPeak);
394 mScript.set_gRotate(mWorldState.mRotate);
395 mScript.set_gTilt(mWorldState.mTilt);
396 mScript.set_gIdle(mWorldState.mIdle);
397 mScript.set_gWaveCounter(mWorldState.mWaveCounter);
398 }
Marco Nelissen838dedb2009-11-03 17:01:21 -0800399}