/** * Copyright 2011, Felix Palmer * <p/> * Licensed under the MIT license: * http://creativecommons.org/licenses/MIT/ */ package com.pheelicks.visualizer; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.media.MediaRecorder; import android.media.audiofx.Visualizer; import android.os.Build; import android.util.AttributeSet; import android.util.Log; import android.view.View; import com.pheelicks.visualizer.renderer.Renderer; import java.util.HashSet; import java.util.Set; import it.angelic.soulissclient.Constants; import it.angelic.soulissclient.fragments.AbstractMusicVisualizerFragment; import it.angelic.soulissclient.helpers.SoulissPreferenceHelper; /** * A class that draws visualizations of data received from a * {@link Visualizer.OnDataCaptureListener#onWaveFormDataCapture } and * {@link Visualizer.OnDataCaptureListener#onFftDataCapture } */ public class VisualizerView extends View { float absMax_low = 300; float absMax_med = 300; float absMax_high = 300; int k_low = 30; int k_med = 1; int k_high = 1; Bitmap mCanvasBitmap; Canvas mCanvas; Matrix neo = new Matrix(); // private Paint transPainter; private byte[] mBytes; AudioData audioData = new AudioData(mBytes); private byte[] mFFTBytes; FFTData fftData = new FFTData(mFFTBytes); //private Paint mFlashPaint = new Paint(); private Paint mFadePaint = new Paint(); private Rect mRect = new Rect(); // non devono essere azzerate ad ogni iterazione!!! private Set<Renderer> mRenderers; private Visualizer mVisualizer; private SoulissPreferenceHelper opz; private AbstractMusicVisualizerFragment parent; private RecordingSampler recordingSampler; public VisualizerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public VisualizerView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public VisualizerView(Context context) { this(context, null, 0); } public void addRenderer(Renderer renderer) { if (renderer != null) { mRenderers.add(renderer); } } public void clearRenderers() { mRenderers.clear(); } public SoulissPreferenceHelper getOpz() { return opz; } public void setOpz(SoulissPreferenceHelper opz) { this.opz = opz; } private void init() { mBytes = null; mFFTBytes = null; //mFlashPaint.setColor(Color.argb(122, 255, 255, 255)); mFadePaint.setColor(Color.argb(100, 255, 255, 255)); // Adjust alpha to mRenderers = new HashSet<>(); mVisualizer = new Visualizer(0); Log.w(Constants.TAG, "SetCapture Size (FFT MINRANGE):" + mVisualizer.getCaptureSize()); recordingSampler = new RecordingSampler(); // transPainter = new Paint(); // transPainter.setXfermode(new PorterDuffXfermode(Mode.CLEAR)); } /** * Links the visualizer to a player * <p/> * - MediaPlayer instance to link to */ public void link(final boolean multicast) { // Create the Visualizer object and attach it to our media player. if (opz.getAudioInputChannel() == MediaRecorder.AudioSource.MIC) { Log.w(Constants.TAG, "MIC input selected"); // recordingSampler.setVolumeListener(this); // for custom implements recordingSampler.setSamplingInterval(100); // voice sampling interval recordingSampler.link(this, multicast);// link to visualizer // mVisualizer.setScalingMode(Visualizer.SCALING_MODE_NORMALIZED); } else { Log.w(Constants.TAG, "default audio input selected"); // Pass through Visualizer data to VisualizerView Visualizer.OnDataCaptureListener captureListener = new Visualizer.OnDataCaptureListener() { @Override public void onFftDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) { byte[] copy = new byte[bytes.length / 2]; System.arraycopy(bytes, 0, copy, 0, copy.length); updateVisualizerFFT(copy); sendSoulissPlinio(copy, multicast); } @Override public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) { updateVisualizer(bytes); Log.w(Constants.TAG, "should not run this"); } }; int dcRate = Visualizer.getMaxCaptureRate(); if (dcRate < 30000) { Log.w(Constants.TAG, "MAXDCRATE invalid, defaulting to:" + dcRate); } else { dcRate = 30000; // 15kHz } mVisualizer.setDataCaptureListener(captureListener, dcRate, false, true); // Enabled Visualizer and disable when we're done with the stream mVisualizer.setEnabled(true); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // Create canvas once we're ready to draw mRect.set(0, 0, getWidth(), getHeight()); if (mCanvasBitmap == null) { mCanvasBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Config.ARGB_4444); } if (mCanvas == null) { mCanvas = new Canvas(mCanvasBitmap); } // mCanvas.drawRect(0, 0,mCanvasBitmap.getWidth(), // mCanvasBitmap.getHeight(), transPainter); canvas.drawBitmap(mCanvasBitmap, neo, null); // Fade out old contents mCanvas.drawPaint(mFadePaint); if (mBytes != null) { // Render all audio renderers //AudioData audioData = new AudioData(mBytes); audioData.bytes = mBytes; for (Renderer r : mRenderers) { r.render(mCanvas, audioData, mRect); } } if (mFFTBytes != null) { // Render all FFT renderers //FFTData fftData = new FFTData(mFFTBytes); fftData.bytes = mFFTBytes; for (Renderer r : mRenderers) { r.render(mCanvas, fftData, mRect); } } } /** * Call to release the resources used by VisualizerView. Like with the * MediaPlayer it is good practice to call this method */ @TargetApi(Build.VERSION_CODES.GINGERBREAD) public void release() { setEnabled(false); // XXX sposta il disable mVisualizer.release(); recordingSampler.release(); } /** * Magie di Dario. * Dalla trasformata di fourier a tre colori * * @param data FFT * @param multicast se inviare a tutti */ public void sendSoulissPlinio(byte[] data, boolean multicast) { float dbValue_low = 1; float dbValue_medium = 1; float dbValue_high = 1; Log.v(Constants.TAG, "data.length:" + data.length); float low_freq_slider = opz.getEqLowRange() * data.length / 2; float med_freq_slider = opz.getEqMedRange() * data.length / 2; float high_freq_slider = opz.getEqHighRange() * data.length / 2; //Log.v(Constants.TAG, "low_freq_slider:" + low_freq_slider+"med_freq_slider:" + med_freq_slider+"hig_freq_slider:" + high_freq_slider); for (int i = 0; i < data.length / 2 - 1; i++) {// half part is imaginary byte rfk, ifk; if ((i > low_freq_slider / 2) && (i < low_freq_slider)) { rfk = data[2 * i]; ifk = data[2 * (i + 1)]; float magnitude_low = (rfk * rfk + ifk * ifk); dbValue_low += magnitude_low; } if ((i > med_freq_slider / 2) && (i < med_freq_slider)) { // MEDI rfk = data[2 * i]; ifk = data[2 * (i + 1)]; float magnitude_med = (rfk * rfk + ifk * ifk); dbValue_medium += magnitude_med; } if ((i > high_freq_slider / 2) && (i < high_freq_slider)) { // ALTI rfk = data[2 * i]; ifk = data[2 * (i + 1)]; float magnitude_high = (rfk * rfk + ifk * ifk); dbValue_high += magnitude_high; } } Log.v(Constants.TAG, "RAWLOW:" + dbValue_low + " MED:" + dbValue_medium + " HI:" + dbValue_high); dbValue_low /= k_low; dbValue_medium /= k_med; dbValue_high /= k_high; if (dbValue_high > absMax_high) absMax_high = dbValue_high; if (dbValue_medium > absMax_med) absMax_med = dbValue_medium; if (dbValue_low > absMax_low) absMax_low = dbValue_low; try { dbValue_low = 255 * (dbValue_low / absMax_low); dbValue_medium = 255 * (dbValue_medium / absMax_med); dbValue_high = 255 * (dbValue_high / absMax_high); } catch (ArithmeticException e) { dbValue_low = 0; dbValue_medium = 0; dbValue_high = 0; } dbValue_low *= opz.getEqLow(); dbValue_medium *= opz.getEqMed(); dbValue_high *= opz.getEqHigh(); Log.v(Constants.TAG, "LOW:" + dbValue_low + " MED:" + dbValue_medium + " HI:" + dbValue_high); parent.issueRGBCommand(Constants.Typicals.Souliss_T1n_Set, (int) dbValue_low, (int) dbValue_medium, (int) dbValue_high, multicast); } public void setEnabled(boolean in) { try { mVisualizer.setEnabled(in); } catch (Exception e) { Log.e(Constants.TAG, "Errore setEnabled:" + e.getMessage()); } //gestione microfono if (recordingSampler != null) { if (!in) { recordingSampler.stopRecording(); //recordingSampler.release(); } else if (in && opz.getAudioInputChannel() == MediaRecorder.AudioSource.MIC && !recordingSampler.isRecording()) { //recordingSampler = new RecordingSampler(); recordingSampler.startRecording(); } } } public void setFrag(AbstractMusicVisualizerFragment par) { parent = par; } /** * Pass data to the visualizer. Typically this will be obtained from the * Android Visualizer.OnDataCaptureListener call back. See * {@link Visualizer.OnDataCaptureListener#onWaveFormDataCapture } * * @param bytes */ public void updateVisualizer(byte[] bytes) { mBytes = bytes; invalidate(); } /** * Pass FFT data to the visualizer. Typically this will be obtained from the * Android Visualizer.OnDataCaptureListener call back. See * {@link Visualizer.OnDataCaptureListener#onFftDataCapture } * * @param bytes */ public void updateVisualizerFFT(byte[] bytes) { mFFTBytes = bytes; invalidate(); } }