/**
* Audalyzer: an audio analyzer for Android.
* <br>Copyright 2009-2010 Ian Cameron Smith
*
* <p>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation (see COPYING).
*
* <p>This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
package com.zzx.factorytest.view;
import org.hermit.android.instruments.AudioAnalyser;
import org.hermit.android.instruments.AudioAnalyser.OnAudioDataChange;
import org.hermit.android.instruments.InstrumentSurface;
import org.hermit.android.instruments.PowerGauge;
import org.hermit.android.instruments.SonagramGauge;
import org.hermit.android.instruments.SpectrumGauge;
import org.hermit.android.instruments.WaveformGauge;
import org.hermit.dsp.Window;
import android.app.Activity;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
/**
* The main audio analyser view. This class relies on the parent SurfaceRunner
* class to do the bulk of the animation control.
*/
public class InstrumentPanel
extends InstrumentSurface
implements GestureDetector.OnGestureListener,
GestureDetector.OnDoubleTapListener
{
// ******************************************************************** //
// Public Constants.
// ******************************************************************** //
/**
* Definitions of the available window functions.
*/
public enum Instruments {
/** Spectrum Gauge, Power and Wave. */
SPECTRUM_P_W,
/** Sonagram Gauge, Power and Wave. */
SONAGRAM_P_W,
/** Spectrum and Sonagram Gauge. */
SPECTRUM_SONAGRAM,
/** Spectrum Gauge. */
SPECTRUM,
/** Sonagram Gauge. */
SONAGRAM,
}
// ******************************************************************** //
// Constructor.
// ******************************************************************** //
/**
* Create a WindMeter instance.
*
* @param app The application context we're running in.
*/
public InstrumentPanel(Activity app) {
super(app, SURFACE_DYNAMIC);
audioAnalyser = new AudioAnalyser(this);
addInstrument(audioAnalyser);
// On-screen debug stats display.
statsCreate(new String[] { "µs FFT", "Skip/s" });
//Gesture detection
gesturedetector = new GestureDetector(this);
gesturedetector.setOnDoubleTapListener(this);
}
// ******************************************************************** //
// Configuration.
// ******************************************************************** //
/**
* Set the sample rate for this instrument.
*
* @param rate The desired rate, in samples/sec.
*/
public void setSampleRate(int rate) {
audioAnalyser.setSampleRate(rate);
}
/**
* Set the input block size for this instrument.
*
* @param size The desired block size, in samples.
*/
public void setBlockSize(int size) {
audioAnalyser.setBlockSize(size);
}
/**
* Set the spectrum analyser windowing function for this instrument.
*
* @param func The desired windowing function.
* Window.Function.BLACKMAN_HARRIS is a good option.
* Window.Function.RECTANGULAR turns off windowing.
*/
public void setWindowFunc(Window.Function func) {
audioAnalyser.setWindowFunc(func);
}
/**
* Set the decimation rate for this instrument.
*
* @param rate The desired decimation. Only 1 in rate blocks
* will actually be processed.
*/
public void setDecimation(int rate) {
audioAnalyser.setDecimation(rate);
}
/**
* Set the histogram averaging window for this instrument.
*
* @param rate The averaging interval. 1 means no averaging.
*/
public void setAverageLen(int rate) {
audioAnalyser.setAverageLen(rate);
}
/**
* Enable or disable stats display.
*
* @param enable True to display performance stats.
*/
public void setShowStats(boolean enable) {
setDebugPerf(enable);
}
/**
* Set the instruments to display
*
* @param InstrumentPanel.Intruments Choose which ones to display.
*/
public void setInstruments(InstrumentPanel.Instruments i) {
currentInstruments=i;
loadInstruments(currentInstruments);
}
/**
* Load instruments
*
* @param InstrumentPanel.Intruments Choose which ones to display.
*/
private void loadInstruments(InstrumentPanel.Instruments i) {
Log.i(TAG, "Load instruments");
//Stop surface update
onPause();
//Clear surface events
clearGauges();
//Clear analyse events
audioAnalyser.resetGauge();
//Destroy last Gauges
sonagramGauge=null;
spectrumGauge=null;
powerGauge=null;
waveformGauge=null;
//Create instruments, update and refresh
/*if ((i==InstrumentPanel.Instruments.SPECTRUM)||(i==InstrumentPanel.Instruments.SPECTRUM_SONAGRAM)||(i==InstrumentPanel.Instruments.SPECTRUM_P_W)) {
spectrumGauge = audioAnalyser.getSpectrumGauge(this);
addGauge(spectrumGauge);
}*/
// if ((i==InstrumentPanel.Instruments.SONAGRAM)||(i==InstrumentPanel.Instruments.SPECTRUM_SONAGRAM)||(i==InstrumentPanel.Instruments.SONAGRAM_P_W)) {
// sonagramGauge = audioAnalyser.getSonagramGauge(this);
// addGauge(sonagramGauge);
// }
if ((i==InstrumentPanel.Instruments.SPECTRUM_P_W)||(i==InstrumentPanel.Instruments.SONAGRAM_P_W)) {
waveformGauge = audioAnalyser.getWaveformGauge(this);
addGauge(waveformGauge);
powerGauge = audioAnalyser.getPowerGauge(this);
addGauge(powerGauge);
}
//Load current layout in Gauges if they're already define
if ((currentWidth>0)&&(currentHeight>0))
refreshLayout();
//Restart
onResume();
Log.i(TAG, "End instruments loading");
}
// ******************************************************************** //
// Layout Processing.
// ******************************************************************** //
/**
* Lay out the display for a given screen size.
*
* @param width The new width of the surface.
* @param height The new height of the surface.
*/
@Override
protected void layout(int width, int height) {
//Save current layout
currentWidth=width;
currentHeight=height;
refreshLayout();
}
/**
* Lay out the display for the current screen size.
*/
protected void refreshLayout() {
// Make up some layout parameters.
int min = Math.min(currentWidth, currentHeight);
int gutter = min / (min > 400 ? 15 : 20);
// Calculate the layout based on the screen configuration.
if (currentWidth > currentHeight)
layoutLandscape(currentWidth, currentHeight, gutter);
else
layoutPortrait(currentWidth, currentHeight, gutter);
// Set the gauge geometries.
if (waveformGauge!=null)
waveformGauge.setGeometry(waveRect);
if (spectrumGauge!=null)
spectrumGauge.setGeometry(specRect);
if (sonagramGauge!=null)
sonagramGauge.setGeometry(sonaRect);
if (powerGauge!=null)
powerGauge.setGeometry(powerRect);
}
/**
* Lay out the display for a given screen size.
*
* @param width The new width of the surface.
* @param height The new height of the surface.
* @param gutter Spacing to leave between items.
*/
private void layoutLandscape(int width, int height, int gutter) {
int x = gutter;
int y = gutter;
// Divide the display into two columns.
int col = (width - gutter * 3) / 2;
//Init
waveRect = new Rect(0,0,0,0);
specRect = new Rect(0,0,0,0);
sonaRect = new Rect(0,0,0,0);
powerRect = new Rect(0,0,0,0);
if (waveformGauge!=null) {
// Divide the left pane in two.
int row = (height - gutter * 3) / 2;
//Wave+Spectrum+Power or Wave+Sonagram+Power
waveRect = new Rect(x, y, x + col, y + row);
y += row + gutter;
powerRect = new Rect(x, y, x + col, height - gutter);
x += col + gutter;
y = gutter;
//Spectrum or Sonagram fullscreen
if (spectrumGauge!=null)
specRect = new Rect(x, y, x + col, height - gutter);
else
sonaRect = new Rect(x, y, x + col, height - gutter);
} else if ((spectrumGauge!=null)&&(sonagramGauge!=null)) {
//Spectrum + Sonagram
specRect = new Rect(x, y, x + col, height - gutter);
x += col + gutter;
sonaRect = new Rect(x, y, x + col, height - gutter);
} else {
//Spectrum or Sonagram fullscreen
if (spectrumGauge!=null)
specRect = new Rect(x, y, width - gutter, height - gutter);
else
sonaRect = new Rect(x, y, width - gutter, height - gutter);
}
}
/**
* Lay out the display for a given screen size.
*
* @param width The new width of the surface.
* @param height The new height of the surface.
* @param gutter Spacing to leave between items.
*/
private void layoutPortrait(int width, int height, int gutter) {
int x = gutter;
int y = gutter;
// Display one column.
int col = width - gutter * 2;
//Init
waveRect = new Rect(0,0,0,0);
specRect = new Rect(0,0,0,0);
sonaRect = new Rect(0,0,0,0);
powerRect = new Rect(0,0,0,0);
if (waveformGauge!=null) {
// Divide the display into three vertical elements, the
// spectrum or sonagram display being double-height.
int unit = (height - gutter * 4) / 4;
//Wave+Spectrum+Power or Wave+Sonagram+Power
waveRect = new Rect(x, y, x + col, y + unit);
y += unit + gutter;
if (spectrumGauge!=null)
specRect = new Rect(x, y, x + col, y + unit * 2);
else
sonaRect = new Rect(x, y, x + col, y + unit * 2);
y += unit * 2 + gutter;
powerRect = new Rect(x, y, x + col, y + unit);
}
else if ((spectrumGauge!=null)&&(sonagramGauge!=null)) {
// Divide the display into two vertical elements
int unit = (height - gutter * 3) / 2;
//Spectrum + Sonagram
specRect = new Rect(x, y, x + col, y + unit);
y += unit + gutter;
sonaRect = new Rect(x, y, x + col, y + unit);
}
else {
//Spectrum or Sonagram fullscreen
if (spectrumGauge!=null)
specRect = new Rect(x, y, width - gutter, height - gutter);
else
sonaRect = new Rect(x, y, width - gutter, height - gutter);
}
}
// ******************************************************************** //
// Input Handling.
// ******************************************************************** //
/**
* Handle key input.
*
* @param keyCode The key code.
* @param event The KeyEvent object that defines the
* button action.
* @return True if the event was handled, false otherwise.
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return false;
}
/**
* Handle touchscreen input.
*
* @param event The MotionEvent object that defines the action.
* @return True if the event was handled, false otherwise.
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
return gesturedetector.onTouchEvent(event);
}
@Override
public boolean onDown(MotionEvent e) {
//True for propagation to onFling Event
return true;
}
@Override
public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX,float velocityY) {
/*if (isFullScreen)
return false;
final float ev1x = event1.getX();
//final float ev1y = event1.getY();
final float ev2x = event2.getX();
//final float ev2y = event2.getY();
final float xdiff = Math.abs(ev1x - ev2x);
//final float ydiff = Math.abs(ev1y - ev2y);
final float xvelocity = Math.abs(velocityX);
//final float yvelocity = Math.abs(velocityY);
if (xvelocity > this.SWIPE_MIN_VELOCITY && xdiff > this.SWIPE_MIN_DISTANCE) {
if (ev1x > ev2x) { //Swipe Left
Log.i(TAG, "Swipe Left");
//Modulo 3 to avoid fullscreen modes (used with onLongPress)
currentInstruments=Instruments.values()[(currentInstruments.ordinal()+1)%3];
loadInstruments(currentInstruments);
} else { //Swipe Right
Log.i(TAG, "Swipe Right");
//Modulo 3 to avoid fullscreen modes (used with onLongPress)
currentInstruments=Instruments.values()[(3+currentInstruments.ordinal()-1)%3];
loadInstruments(currentInstruments);
}
}*/
return false;
}
@Override
public void onLongPress(MotionEvent e) {
//Vibrate
//vibrator.vibrate(100);
final float x = e.getX();
final float y = e.getY();
if (isFullScreen) {
//Load pref instruments
isFullScreen = false;
loadInstruments(currentInstruments);
} else {
//Load fullscreen instrument
if (specRect.contains((int) x, (int) y)) {
isFullScreen = true;
loadInstruments(InstrumentPanel.Instruments.SPECTRUM);
}
if (sonaRect.contains((int) x, (int) y)) {
isFullScreen = true;
loadInstruments(InstrumentPanel.Instruments.SONAGRAM);
}
}
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,float distanceY) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
return false;
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
return false;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return false;
}
// ******************************************************************** //
// Save and Restore.
// ******************************************************************** //
/**
* Save the state of the panel in the provided Bundle.
*
* @param icicle The Bundle in which we should save our state.
*/
protected void saveState(Bundle icicle) {
// gameTable.saveState(icicle);
}
/**
* Restore the panel's state from the given Bundle.
*
* @param icicle The Bundle containing the saved state.
*/
protected void restoreState(Bundle icicle) {
// gameTable.pause();
// gameTable.restoreState(icicle);
}
// ******************************************************************** //
// Class Data.
// ******************************************************************** //
// Debugging tag.
private static final String TAG = "Audalyzer";
// ******************************************************************** //
// Private Data.
// ******************************************************************** //
//Gesture detection
private GestureDetector gesturedetector = null;
final private int SWIPE_MIN_DISTANCE = 100;
final private int SWIPE_MIN_VELOCITY = 100;
// The current Intruments in pref.
private Instruments currentInstruments = null;
// The current fullscreen state
private boolean isFullScreen =false;
//Current layout
private int currentWidth=0;
private int currentHeight=0;
// Our audio input device.
private final AudioAnalyser audioAnalyser;
// The gauges associated with this instrument.
private WaveformGauge waveformGauge = null;
private SpectrumGauge spectrumGauge = null;
private SonagramGauge sonagramGauge = null;
private PowerGauge powerGauge = null;
// Bounding rectangles for the waveform, spectrum, sonagram, and VU meter displays.
private Rect waveRect = null;
private Rect specRect = null;
private Rect sonaRect = null;
private Rect powerRect = null;
public void setOnAudioDataChange(OnAudioDataChange onAudioDataChange){
audioAnalyser.setOnAudioDataChange(onAudioDataChange);
}
}