]> hydra-www.ietfng.org Git - acmetensortoys-watchviz/commitdiff
Some heavy refactoring
authorNathaniel Wesley Filardo <nwf@cs.jhu.edu>
Fri, 17 Jun 2016 06:19:20 +0000 (02:19 -0400)
committerNathaniel Wesley Filardo <nwf@cs.jhu.edu>
Fri, 17 Jun 2016 06:19:20 +0000 (02:19 -0400)
Move visualizations and surface off to its own library
Add Meta-data tracking classes
Improve Grid visualization by using Avg to scale

18 files changed:
app/build.gradle
app/src/main/java/com/acmetensortoys/watchviz/MainActivity.java
phoneapp/build.gradle
settings.gradle
vizlib/.gitignore [new file with mode: 0644]
vizlib/build.gradle [new file with mode: 0644]
vizlib/proguard-rules.pro [new file with mode: 0644]
vizlib/src/main/AndroidManifest.xml [new file with mode: 0644]
vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/AudioCanvas.java [new file with mode: 0644]
vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/Meta.java [new file with mode: 0644]
vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/RenderCB.java [moved from app/src/main/java/com/acmetensortoys/watchviz/RenderCB.java with 79% similarity]
vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/meta/Avg.java [new file with mode: 0644]
vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/meta/Max.java [new file with mode: 0644]
vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/render/Grid.java [moved from app/src/main/java/com/acmetensortoys/watchviz/render/Grid.java with 60% similarity]
vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/render/WholeMax.java [moved from app/src/main/java/com/acmetensortoys/watchviz/render/WholeMax.java with 86% similarity]
vizlib/src/main/java/org/jtransforms/fft/FloatFFT_1D.java [moved from app/src/main/java/org/jtransforms/fft/FloatFFT_1D.java with 97% similarity]
vizlib/src/main/java/org/jtransforms/utils/CommonUtils.java [moved from app/src/main/java/org/jtransforms/utils/CommonUtils.java with 99% similarity]
vizlib/src/main/res/values/strings.xml [new file with mode: 0644]

index fd20a0deeadbe97dadf5896bd4fc9662500ed94d..ed0441bf0d5cb015a218f37c335d69a5b41937cf 100644 (file)
@@ -26,9 +26,10 @@ dependencies {
     compile fileTree(include: ['*.jar'], dir: 'libs')
     compile 'com.google.android.support:wearable:2.0.0-alpha1'
     compile 'com.google.android.gms:play-services-wearable:9.0.2'
+    compile project(':vizlib')
 }
 
 dependencies {
     // compile 'com.github.wendykierp:JTransforms:3.1' // brought just the bit we need in
-    compile 'org.apache.commons:commons-math3:+'
+    // compile 'org.apache.commons:commons-math3:+' // moved to java.lang.Math instead
 }
\ No newline at end of file
index 4d5020df4eef7153e2e9c416006abe6ea6754f30..b2ec4ffff8dfe7146715dee3a051649b4f016bb2 100644 (file)
 package com.acmetensortoys.watchviz;
 
 import android.Manifest;
-import android.bluetooth.BluetoothManager;
-import android.content.Context;
 import android.content.pm.PackageManager;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.media.AudioFormat;
-import android.media.AudioRecord;
-import android.media.MediaRecorder;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.wearable.activity.WearableActivity;
 import android.support.wearable.view.BoxInsetLayout;
 import android.util.Log;
-import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import com.acmetensortoys.watchviz.render.Grid;
-import com.acmetensortoys.watchviz.render.WholeMax;
-
 import java.text.SimpleDateFormat;
-import java.util.ArrayDeque;
 import java.util.Date;
-import java.util.Deque;
 import java.util.Locale;
 
-import org.jtransforms.fft.FloatFFT_1D;
+import com.acmetensortoys.watchviz.vizlib.AudioCanvas;
 
 public class MainActivity extends WearableActivity
 {
     private static final SimpleDateFormat AMBIENT_DATE_FORMAT =
             new SimpleDateFormat("HH:mm", Locale.US);
 
-    private static final int AUDIO_RECORDER_BUFFER_SIZE = 2048;
-    private static final int AUDIO_SAMPLES = 512;
-
     private BoxInsetLayout mOuterContainer;
     private LinearLayout mInnerContainer;
     private TextView mTextView, mClockView, mDebugView;
-
-    private SurfaceView cyclersv;
-    private Thread cycler;
-
-    private RenderCB cyclercb;
-    private Deque<Class<? extends RenderCB>> cyclercbq = new ArrayDeque<>();
-    private void _setCyclerCB(Class<? extends RenderCB> next) {
-        try {
-            cyclercb = next.getConstructor().newInstance();
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-        String name = cyclercb.getClass().getSimpleName();
-        mDebugView.setText(name.substring(0,Math.min(10,name.length())));
-    }
-    private void nextCyclerCB() {
-        synchronized(this) {
-            Class <? extends RenderCB> next = cyclercbq.removeFirst();
-            cyclercbq.addLast(next);
-            _setCyclerCB(next);
-        }
-    }
-    private void prevCyclerCB() {
-        synchronized(this) {
-            Class <? extends RenderCB> next = cyclercbq.removeLast();
-            cyclercbq.addFirst(next);
-            _setCyclerCB(next);
-        }
-    }
-    {
-        cyclercbq.add(WholeMax.class);
-        cyclercbq.add(Grid.class);
-    }
-
-    // The surface to which this callback is bound is created only after audio permissions
-    // have been checked.  We therefore can simply start in the created callback.  Stopping
-    // happens in onPause below, which fires before the surface is destroyed.
-    private SurfaceHolder.Callback shc = new SurfaceHolder.Callback() {
-        @Override
-        public void surfaceCreated(final SurfaceHolder h) {
-            Log.d("shc", "Surface Created");
-            Canvas c = h.lockCanvas();
-            c.drawColor(Color.RED);
-            h.unlockCanvasAndPost(c);
-
-            final AudioRecord ar = new AudioRecord(
-                    MediaRecorder.AudioSource.MIC,
-                    11025, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_FLOAT,
-                    AUDIO_RECORDER_BUFFER_SIZE);
-            Log.d("shc", "Audio session ID:" + ar.getAudioSessionId());
-
-            cycler = new Thread() {
-                public void run() {
-                    // Raw audio samples
-                    float[] samples = new float[AUDIO_SAMPLES];
-
-                    // FFT data and engine
-                    float[] fft = new float[AUDIO_SAMPLES];
-                    FloatFFT_1D fftc = new FloatFFT_1D(AUDIO_SAMPLES);
-
-                    ar.startRecording();
-                    while (!Thread.interrupted()) {
-                        final RenderCB rcb;
-                        ar.read(samples, 0, samples.length, AudioRecord.READ_BLOCKING);
-                        System.arraycopy(samples,0,fft,0,AUDIO_SAMPLES);
-                        /*
-                         * Debug: triangle wave
-                         */
-                        /*
-                        for(int i = 0; i < samples.length; i++) {
-                            samples[i] = (float)((i % 16) - 8) / 8;
-                            if (i % 32 >= 16) { samples[i] *= -1; }
-                        }
-                        */
-
-                        fftc.realForward(fft);
-
-                        synchronized(this) {
-                            rcb = cyclercb;
-                        }
-
-                        Canvas cv = h.lockCanvas();
-                        if (cv == null) {
-                            // the surface must have been destroyed out from under us;
-                            // just stop here.
-                            break;
-                        }
-                        cv.drawColor(Color.BLACK);
-                        rcb.render(cv,samples,fft);
-                        h.unlockCanvasAndPost(cv);
-
-                        // try { Thread.sleep(100); } catch (InterruptedException e) { break; }
-                    }
-                    ar.stop();
-                    ar.release();
-                    Log.d("cycler", "exit");
-                }
-            };
-            cycler.start();
-        }
-
-        @Override
-        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
-            Log.d("shc", "Surface Changed");
-            ;
-        }
-
-        @Override
-        public void surfaceDestroyed(SurfaceHolder holder) {
-            Log.d("shc", "Surface Destroyed");
-        }
-    };
+    private SurfaceView mACSurfaceView;
+    private AudioCanvas mAudioCanvas;
 
     private void createSurface() {
         Log.d("createSurface", "top");
-
-        nextCyclerCB();
-
-        cyclersv = new SurfaceView(this);
-        cyclersv.getHolder().addCallback(shc);
-        cyclersv.setOnClickListener(new View.OnClickListener(){
+        mACSurfaceView = new SurfaceView(this);
+        mAudioCanvas = new AudioCanvas(mDebugView, mACSurfaceView);
+        mTextView.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                final RenderCB rcb;
-                synchronized(this) { rcb = cyclercb; }
-                rcb.onClick();
+                mAudioCanvas.prevCyclerCB();
             }
         });
-        mTextView.setOnClickListener(new View.OnClickListener() {
+        mACSurfaceView.setOnClickListener(new View.OnClickListener(){
             @Override
-            public void onClick(View v) {
-                prevCyclerCB();
+            public void onClick(View v) { mAudioCanvas.onClick(); }
+        });
+        mACSurfaceView.setOnLongClickListener(new View.OnLongClickListener() {
+            @Override
+            public boolean onLongClick(View v) {
+                mAudioCanvas.nextCyclerCB();
+                return true;
             }
         });
-        cyclersv.setOnLongClickListener(new View.OnLongClickListener() {
-                    @Override
-                    public boolean onLongClick(View v) {
-                        nextCyclerCB();
-                        return true;
-                    }
-                });
-
-        mInnerContainer.addView(cyclersv, -1,
+        mInnerContainer.addView(mACSurfaceView, -1,
                 new ViewGroup.LayoutParams(
                         ViewGroup.LayoutParams.MATCH_PARENT,
                         ViewGroup.LayoutParams.MATCH_PARENT));
     }
 
     private void removeSurface() {
-        Log.d("removeSurface", "stopping cycler");
-        if(cycler != null) {
-            cycler.interrupt();
-            try { cycler.join(); }
-            catch (InterruptedException e) { Log.d("shc", "IE while join cycler"); }
-        }
         Log.d("removeSurface", "removing view");
-        mInnerContainer.removeView(cyclersv);
-        cyclersv = null;
+        mInnerContainer.removeView(mACSurfaceView);
         Log.d("removeSurface", "done");
     }
 
index e46e822fd6cc222437a433b2efb08d84ba8514ad..df5573a123b21eb1eed50fb6ec08c6df59293038 100644 (file)
@@ -6,7 +6,7 @@ android {
 
     defaultConfig {
         applicationId "com.acmetensortoys.watchviz"
-        minSdkVersion 9
+        minSdkVersion 23
         targetSdkVersion 23
         versionCode 1
         versionName "1.0"
@@ -20,8 +20,9 @@ android {
 }
 
 dependencies {
-    compile fileTree(dir: 'libs', include: ['*.jar'])
+    compile fileTree(include: ['*.jar'], dir: 'libs')
     compile 'com.android.support:appcompat-v7:23.4.0'
+    compile project(':vizlib')
 }
 
 dependencies {
index b8c9776dbd920f7d0ac20ad1361d2b0f18315e49..c007f840dae8c5c730a6eae1d58096cf82364815 100644 (file)
@@ -1 +1 @@
-include ':app', ':phoneapp'
+include ':vizlib', ':app', ':phoneapp'
diff --git a/vizlib/.gitignore b/vizlib/.gitignore
new file mode 100644 (file)
index 0000000..796b96d
--- /dev/null
@@ -0,0 +1 @@
+/build
diff --git a/vizlib/build.gradle b/vizlib/build.gradle
new file mode 100644 (file)
index 0000000..b279fba
--- /dev/null
@@ -0,0 +1,24 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion 23
+    buildToolsVersion "23.0.3"
+
+    defaultConfig {
+        minSdkVersion 23
+        targetSdkVersion 23
+        versionCode 1
+        versionName "1.0"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    testCompile 'junit:junit:4.12'
+}
diff --git a/vizlib/proguard-rules.pro b/vizlib/proguard-rules.pro
new file mode 100644 (file)
index 0000000..23c6bbc
--- /dev/null
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /home/nwf/Android/Sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/vizlib/src/main/AndroidManifest.xml b/vizlib/src/main/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..27c719d
--- /dev/null
@@ -0,0 +1,11 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.acmetensortoys.vizlib">
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+>
+
+    </application>
+
+</manifest>
diff --git a/vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/AudioCanvas.java b/vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/AudioCanvas.java
new file mode 100644 (file)
index 0000000..f498ff9
--- /dev/null
@@ -0,0 +1,157 @@
+package com.acmetensortoys.watchviz.vizlib;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.MediaRecorder;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.widget.TextView;
+
+import com.acmetensortoys.watchviz.vizlib.render.Grid;
+import com.acmetensortoys.watchviz.vizlib.render.WholeMax;
+
+import org.jtransforms.fft.FloatFFT_1D;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+public class AudioCanvas {
+    private TextView mDebugView;
+    private SurfaceView mSurfaceView;
+    
+    private RenderCB renderCB;
+    private Deque<Class<? extends RenderCB>> cbq = new ArrayDeque<>();
+    private Thread cycler;
+
+    public AudioCanvas(TextView debugView, SurfaceView surfaceView) {
+        this.mDebugView = debugView;
+        this.mSurfaceView = surfaceView;
+
+        mSurfaceView.getHolder().addCallback(shc);
+
+        cbq.add(Grid.class);
+        cbq.add(WholeMax.class);
+        nextCyclerCB();
+    }
+
+    private void _setCyclerCB(Class<? extends RenderCB> next) {
+        try {
+            renderCB = next.getConstructor().newInstance();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        String name = renderCB.getClass().getSimpleName();
+        mDebugView.setText(name.substring(0,Math.min(10,name.length())));
+    }
+    public void nextCyclerCB() {
+        synchronized(this) {
+            Class <? extends RenderCB> next = cbq.removeFirst();
+            cbq.addLast(next);
+            _setCyclerCB(next);
+        }
+    }
+    public void prevCyclerCB() {
+        synchronized(this) {
+            Class <? extends RenderCB> next = cbq.removeLast();
+            cbq.addFirst(next);
+            _setCyclerCB(next);
+        }
+    }
+    public void onClick() {
+        final RenderCB rcb;
+        synchronized(this) { rcb = renderCB; }
+        rcb.onClick();
+    }
+
+    // The surface to which this callback is bound is created only after audio permissions
+    // have been checked.  We therefore can simply start in the created callback.  Stopping
+    // happens in onPause below, which fires before the surface is destroyed.
+    private SurfaceHolder.Callback shc = new SurfaceHolder.Callback() {
+
+        private static final int AUDIO_RECORDER_BUFFER_SIZE = 2048;
+        private static final int AUDIO_SAMPLES = 512;
+
+        @Override
+        public void surfaceCreated(final SurfaceHolder h) {
+            Log.d("shc", "Surface Created");
+            Canvas c = h.lockCanvas();
+            c.drawColor(Color.RED);
+            h.unlockCanvasAndPost(c);
+
+            final AudioRecord ar = new AudioRecord(
+                    MediaRecorder.AudioSource.MIC,
+                    11025, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_FLOAT,
+                    AUDIO_RECORDER_BUFFER_SIZE);
+            Log.d("shc", "Audio session ID:" + ar.getAudioSessionId());
+
+            cycler = new Thread() {
+                public void run() {
+                    // Raw audio samples
+                    float[] samples = new float[AUDIO_SAMPLES];
+
+                    // FFT data and engine
+                    float[] fft = new float[AUDIO_SAMPLES];
+                    FloatFFT_1D fftc = new FloatFFT_1D(AUDIO_SAMPLES);
+
+                    ar.startRecording();
+                    while (!Thread.interrupted()) {
+                        final RenderCB rcb;
+                        ar.read(samples, 0, samples.length, AudioRecord.READ_BLOCKING);
+                        System.arraycopy(samples,0,fft,0,AUDIO_SAMPLES);
+                        /*
+                         * Debug: triangle wave
+                         */
+                        /*
+                        for(int i = 0; i < samples.length; i++) {
+                            samples[i] = (float)((i % 16) - 8) / 8;
+                            if (i % 32 >= 16) { samples[i] *= -1; }
+                        }
+                        */
+
+                        fftc.realForward(fft);
+
+                        synchronized(this) {
+                            rcb = renderCB;
+                        }
+
+                        Canvas cv = h.lockCanvas();
+                        if (cv == null) {
+                            // the surface must have been destroyed out from under us;
+                            // just stop here.
+                            break;
+                        }
+                        cv.drawColor(Color.BLACK);
+                        rcb.render(cv,samples,fft);
+                        h.unlockCanvasAndPost(cv);
+
+                        // try { Thread.sleep(100); } catch (InterruptedException e) { break; }
+                    }
+                    ar.stop();
+                    ar.release();
+                    Log.d("cycler", "exit");
+                }
+            };
+            cycler.start();
+        }
+
+        @Override
+        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+            Log.d("shc", "Surface Changed");
+            ;
+        }
+
+        @Override
+        public void surfaceDestroyed(SurfaceHolder holder) {
+            Log.d("shc", "Surface Destroyed");
+            if(cycler != null) {
+                cycler.interrupt();
+                try { cycler.join(); }
+                catch (InterruptedException e) { Log.d("shc", "IE while join cycler"); }
+            }
+        }
+    };
+}
diff --git a/vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/Meta.java b/vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/Meta.java
new file mode 100644 (file)
index 0000000..f90b9b8
--- /dev/null
@@ -0,0 +1,6 @@
+package com.acmetensortoys.watchviz.vizlib;
+
+public interface Meta {
+    float get();
+    void update(float last);
+}
similarity index 79%
rename from app/src/main/java/com/acmetensortoys/watchviz/RenderCB.java
rename to vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/RenderCB.java
index b61723a8e68c73c2e9ff202e09a5939eca70e7fc..ace217b51bf048e0ea28a0dc1da43c47249d78ab 100644 (file)
@@ -1,4 +1,4 @@
-package com.acmetensortoys.watchviz;
+package com.acmetensortoys.watchviz.vizlib;
 
 import android.graphics.Canvas;
 
diff --git a/vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/meta/Avg.java b/vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/meta/Avg.java
new file mode 100644 (file)
index 0000000..09424bf
--- /dev/null
@@ -0,0 +1,33 @@
+package com.acmetensortoys.watchviz.vizlib.meta;
+
+import com.acmetensortoys.watchviz.vizlib.Meta;
+
+public class Avg implements Meta {
+    public float window = 0f;
+    public int ix = 0;
+    public float[] list;
+
+    public Avg(int shift) {
+        list = new float[1<<shift];
+    }
+
+    @Override
+    public float get() {
+        return window / list.length;
+    }
+
+    @Override
+    public void update(float last) {
+        window -= list[ix];
+        list[ix] = last;
+
+        if(ix == list.length-1) {
+            window = 0;
+            for(float v : list) { window += v; }
+            ix = 0;
+        } else {
+            window += list[ix];
+            ix += 1;
+        }
+    }
+}
diff --git a/vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/meta/Max.java b/vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/meta/Max.java
new file mode 100644 (file)
index 0000000..e2b3088
--- /dev/null
@@ -0,0 +1,39 @@
+package com.acmetensortoys.watchviz.vizlib.meta;
+
+import com.acmetensortoys.watchviz.vizlib.Meta;
+
+public class Max implements Meta {
+    public float window = Float.NEGATIVE_INFINITY;
+    public int ix = 0;
+    final public float[] list;
+
+    public Max(int shift) {
+        if (shift < 0 || shift > 16) {
+            throw new RuntimeException("Improper shift: " + shift);
+        }
+        list = new float[1<<shift];
+    }
+
+    @Override
+    public float get() {
+        return window;
+    }
+
+    @Override
+    public void update(float last) {
+        if (last > window) {
+            // If new thing is bigger, set both...
+            window = list[ix] = last;
+        } else if (list[ix] < window) {
+            // if smaller but current ix is not witness...
+            list[ix] = last;
+        } else {
+            // Whoop, smaller and current thing maybe is the witness...
+            list[ix] = last;
+            window = last;
+            for (float f : list) { window = Math.max(window, f); }
+        }
+
+        ix = (ix + 1) % list.length;
+    }
+}
similarity index 60%
rename from app/src/main/java/com/acmetensortoys/watchviz/render/Grid.java
rename to vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/render/Grid.java
index ff9ad4b5efc87787749f6aba50da7de6a6ecdad3..cdd64cda6be40c6fbac8c2aa9bcfcc5c0f42db43 100644 (file)
@@ -1,10 +1,11 @@
-package com.acmetensortoys.watchviz.render;
+package com.acmetensortoys.watchviz.vizlib.render;
 
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 
-import com.acmetensortoys.watchviz.RenderCB;
+import com.acmetensortoys.watchviz.vizlib.RenderCB;
+import com.acmetensortoys.watchviz.vizlib.meta.Avg;
 
 public final class Grid extends RenderCB {
     private boolean doDebug;
@@ -12,6 +13,10 @@ public final class Grid extends RenderCB {
     private final Paint dbp = new Paint();
     private float[] hsv = new float[]{0.0f, 1.0f, 1.0f};
 
+    // 2^7 == 128 frames, at 512 samples per frame and 11025 KHz, this works out
+    // to six seconds, which seems fine.
+    private Avg meta = new Avg(7);
+
     public Grid()
     {
         dbp.setColor(Color.WHITE);
@@ -28,21 +33,30 @@ public final class Grid extends RenderCB {
         hsv[0] = hsv[0] >= 359 ? 0.0f : hsv[0] + 1.0f;
         p.setColor(c);
 
-        final float scale = 32;
+        float thisFrameSum = 0f;
+        final float winAvg = meta.get();
 
         int rxs = cv.getWidth() / 8;
         int rys = cv.getHeight() / 8;
         for (int rx = 0; rx < 8; rx++) {
             for (int ry = 0; ry < 8; ry++) {
-                int ix = (rx * 8 + ry) * 4;
-                float x = (Math.abs(fft[ix]) + Math.abs(fft[ix+2])) * scale;
-                int b = x > 255 ? 255 : (int)x;
-                p.setAlpha(b > 223 ? 255 : b + 32);
+                int ix = (rx * 8 + ry) * 4 + 2;
+                float x = (Math.abs(fft[ix]) + Math.abs(fft[ix+2]));
+
+                thisFrameSum += x;
+
+                // Two points define a line: (0 -> 0x20), (meta -> 0x60), cap at 0xFF.
+                float sx = (0x40 / winAvg) * x + 0x20;
+                int b = sx > 255 ? 255 : (int)sx;
+
+                p.setAlpha(b);
                 cv.drawRect(rx * rxs, ry * rys, (rx + 1) * rxs - 1, (ry + 1) * rys - 1, p);
                 if(doDebug) {
                     cv.drawText(Integer.toHexString(b), rx*rxs + rxs/2, ry*rys + rys/2, dbp);
                 }
             }
         }
+
+        meta.update(thisFrameSum/64);
     }
 };
similarity index 86%
rename from app/src/main/java/com/acmetensortoys/watchviz/render/WholeMax.java
rename to vizlib/src/main/java/com/acmetensortoys/watchviz/vizlib/render/WholeMax.java
index 04e5bd96248261de143b041769dbd6a129921acc..77dba4f6bcdf5d64b9556a50219b3ac8f7064610 100644 (file)
@@ -1,11 +1,11 @@
-package com.acmetensortoys.watchviz.render;
+package com.acmetensortoys.watchviz.vizlib.render;
 
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 
-import com.acmetensortoys.watchviz.RenderCB;
+import com.acmetensortoys.watchviz.vizlib.RenderCB;
 
 import java.util.Locale;
 
@@ -28,8 +28,8 @@ public final class WholeMax extends RenderCB {
     public void render(Canvas cv, float[] audio, float[] fft) {
         float msamp = 0.0f;
         int mix = -1;
-            /* Restrict search to lowest half in agreement with Grid */
-        for (int i = 0; i < fft.length/2; i += 2) {
+            /* Restrict search to lowest half in agreement with Grid, and skip DC component */
+        for (int i = 2; i < fft.length/2; i += 2) {
             if (fft[i] > msamp) {
                 msamp = fft[i];
                 mix = i;
similarity index 97%
rename from app/src/main/java/org/jtransforms/fft/FloatFFT_1D.java
rename to vizlib/src/main/java/org/jtransforms/fft/FloatFFT_1D.java
index aa6a990126c7f7d6d725a3c5f307aa08f0a6fa96..b37eb5c4df49bbcfcf27fb0381e76bc2ca660469 100644 (file)
@@ -27,7 +27,8 @@
 package org.jtransforms.fft;\r
 \r
 import org.jtransforms.utils.CommonUtils;\r
-import static org.apache.commons.math3.util.FastMath.*;\r
+// import static org.apache.commons.math3.util.FastMath.*;\r
+import static java.lang.Math.*;\r
 \r
 /**\r
  * Computes 1D Discrete Fourier Transform (DFT) of complex and real, single\r
similarity index 99%
rename from app/src/main/java/org/jtransforms/utils/CommonUtils.java
rename to vizlib/src/main/java/org/jtransforms/utils/CommonUtils.java
index 31baad5859162e0e550c9cc50b59b8a537a6af89..b3ebc035f50bb982eccd50c8fd9cd04caa283a68 100644 (file)
@@ -26,7 +26,8 @@
  * ***** END LICENSE BLOCK ***** */
 package org.jtransforms.utils;
 
-import static org.apache.commons.math3.util.FastMath.*;
+// import static org.apache.commons.math3.util.FastMath.*;
+import static java.lang.Math.*;
 
 /**
  * Static utility methods.
diff --git a/vizlib/src/main/res/values/strings.xml b/vizlib/src/main/res/values/strings.xml
new file mode 100644 (file)
index 0000000..dbd1c27
--- /dev/null
@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">Viz Lib</string>
+</resources>