2013-01-12 19 views
9

Ho bisogno di creare un'applicazione Android che sia per la registrazione della voce mentre mostri la visualizzazione del livello di voce (suono).Android: registrazione audio con visualizzazione a livello di voce

Ho già creato un'applicazione di registrazione audio ma non riesco ad aggiungervi la visualizzazione del livello audio. Come posso farlo?

per favore qualcuno mi aiuti un suggerimento o un esempio di tutorial o un codice di esempio.

risposta

2

Se si utilizza la classe MediaRecorder e la visualizzazione in base all'ampiezza di picco è corretta, è possibile utilizzare il metodo getMaxAmplitude() per eseguire il polling continuo della "massima ampiezza assoluta campionata dall'ultima chiamata".
Ridimensiona l'ampiezza in un indice che determina quante barre del volume grafico della tua app si accendono e sei impostato.

+1

C'è qualche codice di esempio? – user1972690

+1

Non ho alcun codice di esempio per questo. Ma la maggior parte di esso sembrerebbe una semplice codifica dell'interfaccia utente di Android. Avviare un 'Handler' che [si esegue periodicamente] (http://stackoverflow.com/questions/6242268/repeat-a-task-with-a-time-delay/6242292#6242292), chiamare il metodo' getMaxAmplitude' per il tuo 'MediaRecorder' per ottenere il livello di picco corrente, aggiorna il' View' che contiene le barre del volume in base al nuovo livello di picco, e infine fai un [postInvalidate] (http://stackoverflow.com/questions/5521596/what- does-postinvalidate-do) per richiedere che la Vista modificata sia ridisegnata. – Michael

+0

@Michael Questo sembra avere molto senso, non sono sicuro di quale id audioSession utilizzare per inizializzare il Visualizer. –

28

Creare un xml activity_recording.xml come questo.

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="180dp" 
    android:layout_alignParentBottom="true" 
    android:background="#231f20" > 

    <ali.visualiser.VisualizerView 
     android:id="@+id/visualizer" 
     android:layout_width="220dp" 
     android:layout_height="75dp" 
     android:layout_centerHorizontal="true" 
     android:layout_margin="5dp" /> 

    <TextView 
     android:id="@+id/txtRecord" 
     android:layout_width="wrap_content" 
     android:layout_height="40dp" 
     android:layout_alignParentBottom="true" 
     android:layout_centerHorizontal="true" 
     android:layout_marginBottom="25dp" 
     android:gravity="center" 
     android:text="Start Recording" 
     android:textColor="@android:color/white" 
     android:textSize="30sp" /> 

</RelativeLayout> 

Creare un visualizerView personalizzato come indicato di seguito.

package ali.visualiser; 

import java.util.ArrayList; 
import java.util.List; 

import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Paint; 
import android.util.AttributeSet; 
import android.view.View; 

public class VisualizerView extends View { 
    private static final int LINE_WIDTH = 1; // width of visualizer lines 
    private static final int LINE_SCALE = 75; // scales visualizer lines 
    private List<Float> amplitudes; // amplitudes for line lengths 
    private int width; // width of this View 
    private int height; // height of this View 
    private Paint linePaint; // specifies line drawing characteristics 

    // constructor 
    public VisualizerView(Context context, AttributeSet attrs) { 
     super(context, attrs); // call superclass constructor 
     linePaint = new Paint(); // create Paint for lines 
     linePaint.setColor(Color.GREEN); // set color to green 
     linePaint.setStrokeWidth(LINE_WIDTH); // set stroke width 
    } 

    // called when the dimensions of the View change 
    @Override 
    protected void onSizeChanged(int w, int h, int oldw, int oldh) { 
     width = w; // new width of this View 
     height = h; // new height of this View 
     amplitudes = new ArrayList<Float>(width/LINE_WIDTH); 
    } 

    // clear all amplitudes to prepare for a new visualization 
    public void clear() { 
     amplitudes.clear(); 
    } 

    // add the given amplitude to the amplitudes ArrayList 
    public void addAmplitude(float amplitude) { 
     amplitudes.add(amplitude); // add newest to the amplitudes ArrayList 

     // if the power lines completely fill the VisualizerView 
     if (amplitudes.size() * LINE_WIDTH >= width) { 
      amplitudes.remove(0); // remove oldest power value 
     } 
    } 

    // draw the visualizer with scaled lines representing the amplitudes 
    @Override 
    public void onDraw(Canvas canvas) { 
     int middle = height/2; // get the middle of the View 
     float curX = 0; // start curX at zero 

     // for each item in the amplitudes ArrayList 
     for (float power : amplitudes) { 
      float scaledHeight = power/LINE_SCALE; // scale the power 
      curX += LINE_WIDTH; // increase X by LINE_WIDTH 

      // draw a line representing this item in the amplitudes ArrayList 
      canvas.drawLine(curX, middle + scaledHeight/2, curX, middle 
        - scaledHeight/2, linePaint); 
     } 
    } 

} 

Creare la classe RecordingActivity come indicato di seguito.

pacchetto ali.visualiser;

import java.io.File; 
import java.io.IOException; 

import android.app.Activity; 
import android.media.MediaRecorder; 
import android.media.MediaRecorder.OnErrorListener; 
import android.media.MediaRecorder.OnInfoListener; 
import android.os.Bundle; 
import android.os.Environment; 
import android.os.Handler; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.TextView; 



public class RecordingActivity extends Activity { 
    public static final String DIRECTORY_NAME_TEMP = "AudioTemp"; 
    public static final int REPEAT_INTERVAL = 40; 
    private TextView txtRecord; 

    VisualizerView visualizerView; 

    private MediaRecorder recorder = null; 

    File audioDirTemp; 
private boolean isRecording = false; 


    private Handler handler; // Handler for updating the visualizer 
    // private boolean recording; // are we currently recording? 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_recording); 

     visualizerView = (VisualizerView) findViewById(R.id.visualizer); 

     txtRecord = (TextView) findViewById(R.id.txtRecord); 
     txtRecord.setOnClickListener(recordClick); 

     audioDirTemp = new File(Environment.getExternalStorageDirectory(), 
       DIRECTORY_NAME_TEMP); 
     if (audioDirTemp.exists()) { 
      deleteFilesInDir(audioDirTemp); 
     } else { 
      audioDirTemp.mkdirs(); 
     } 

     // create the Handler for visualizer update 
     handler = new Handler(); 
    } 

    OnClickListener recordClick = new OnClickListener() { 

     @Override 
     public void onClick(View v) { 

      if (!isRecording) { 
       // isRecording = true; 

       txtRecord.setText("Stop Recording"); 

       recorder = new MediaRecorder(); 

       recorder.setAudioSource(MediaRecorder.AudioSource.MIC); 
       recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); 
       recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); 
       recorder.setOutputFile(audioDirTemp + "/audio_file" 
         + ".mp3"); 

       OnErrorListener errorListener = null; 
       recorder.setOnErrorListener(errorListener); 
       OnInfoListener infoListener = null; 
       recorder.setOnInfoListener(infoListener); 

       try { 
        recorder.prepare(); 
        recorder.start(); 
        isRecording = true; // we are currently recording 
       } catch (IllegalStateException e) { 
        e.printStackTrace(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
       handler.post(updateVisualizer); 

      } else { 

       txtRecord.setText("Start Recording"); 

       releaseRecorder(); 
      } 

     } 
    }; 

    private void releaseRecorder() { 
     if (recorder != null) { 
      isRecording = false; // stop recording 
      handler.removeCallbacks(updateVisualizer); 
      visualizerView.clear(); 
      recorder.stop(); 
      recorder.reset(); 
      recorder.release(); 
      recorder = null; 
     } 
    } 

    public static boolean deleteFilesInDir(File path) { 

     if(path.exists()) { 
      File[] files = path.listFiles(); 
      if (files == null) { 
       return true; 
      } 
      for(int i=0; i<files.length; i++) { 

       if(files[i].isDirectory()) {     

       } 
       else { 
        files[i].delete(); 
       } 
      } 
     } 
     return true; 
    } 

    @Override 
    protected void onDestroy() { 

     super.onDestroy(); 
     releaseRecorder(); 
    } 

    // updates the visualizer every 50 milliseconds 
    Runnable updateVisualizer = new Runnable() { 
     @Override 
     public void run() { 
      if (isRecording) // if we are already recording 
      { 
       // get the current amplitude 
       int x = recorder.getMaxAmplitude(); 
       visualizerView.addAmplitude(x); // update the VisualizeView 
       visualizerView.invalidate(); // refresh the VisualizerView 

       // update in 40 milliseconds 
       handler.postDelayed(this, REPEAT_INTERVAL); 
      } 
     } 
    }; 


} 

Risultato

Questo è come appare: https://www.youtube.com/watch?v=BoFG6S02GH0

Quando si raggiunge la fine, l'animazione continua come previsto: cancellare l'inizio del grafico.

+2

non dimenticare di aggiungere l'autorizzazione Ali

+1

Sarebbe fantastico se tu, o chiunque usi la tua soluzione, ha aggiunto uno screenshot di un esempio funzionante – vedant1811

+0

Questo non funziona. quando inizia la registrazione, la registrazione non si avvia. C'è un'eccezione quando la interrompi. Per favore aiutatemi. L'eccezione è riportata sotto –

3

Mi piace la risposta di Ali, ma ecco una versione più semplice che funziona molto meglio.

Ho lanciato la mia RecordingActivity e l'ho impostata a schermo intero, ma puoi creare una risorsa di layout o aggiungere la vista ovunque.

RecordingActivity with Fullscreen View

public class RecordingActivity extends Activity { 
    private VisualizerView visualizerView; 
    private MediaRecorder recorder = new MediaRecorder(); 
    private Handler handler = new Handler(); 
    final Runnable updater = new Runnable() { 
     public void run() { 
      handler.postDelayed(this, 1); 
      int maxAmplitude = recorder.getMaxAmplitude(); 
      if (maxAmplitude != 0) { 
       visualizerView.addAmplitude(maxAmplitude); 
      } 
     } 
    }; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_recording); 
     visualizerView = (VisualizerView) findViewById(R.id.visualizer); 
     try { 
      recorder.setAudioSource(MediaRecorder.AudioSource.MIC); 
      recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); 
      recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); 
      recorder.setOutputFile("/dev/null"); 
      recorder.prepare(); 
      recorder.start(); 
     } catch (IllegalStateException | IOException ignored) { 
     } 
    } 

    @Override 
    protected void onDestroy() { 
     super.onDestroy(); 
     handler.removeCallbacks(updater); 
     recorder.stop(); 
     recorder.reset(); 
     recorder.release(); 
    } 

    @Override 
    public void onWindowFocusChanged(boolean hasFocus) { 
     super.onWindowFocusChanged(hasFocus); 
     handler.post(updater); 
    } 
} 

metodo OnDraw del classe di visualizzazione dovrebbe essere il più veloce possibile.

public class VisualizerView extends View { 
    private static final int MAX_AMPLITUDE = 32767; 

    private float[] amplitudes; 
    private float[] vectors; 
    private int insertIdx = 0; 
    private Paint pointPaint; 
    private Paint linePaint; 
    private int width; 
    private int height; 

    public VisualizerView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     linePaint = new Paint(); 
     linePaint.setColor(Color.GREEN); 
     linePaint.setStrokeWidth(1); 
     pointPaint = new Paint(); 
     pointPaint.setColor(Color.BLUE); 
     pointPaint.setStrokeWidth(1); 
    } 

    @Override 
    protected void onSizeChanged(int width, int h, int oldw, int oldh) { 
     this.width = width; 
     height = h; 
     amplitudes = new float[this.width * 2]; // xy for each point across the width 
     vectors = new float[this.width * 4]; // xxyy for each line across the width 
    } 

    /** 
    * modifies draw arrays. cycles back to zero when amplitude samples reach max screen size 
    */ 
    public void addAmplitude(int amplitude) { 
     invalidate(); 
     float scaledHeight = ((float) amplitude/MAX_AMPLITUDE) * (height - 1); 
     int ampIdx = insertIdx * 2; 
     amplitudes[ampIdx++] = insertIdx; // x 
     amplitudes[ampIdx] = scaledHeight; // y 
     int vectorIdx = insertIdx * 4; 
     vectors[vectorIdx++] = insertIdx; // x0 
     vectors[vectorIdx++] = 0;   // y0 
     vectors[vectorIdx++] = insertIdx; // x1 
     vectors[vectorIdx] = scaledHeight; // y1 
     // insert index must be shorter than screen width 
     insertIdx = ++insertIdx >= width ? 0 : insertIdx; 
    } 

    @Override 
    public void onDraw(Canvas canvas) { 
     canvas.drawLines(vectors, linePaint); 
     canvas.drawPoints(amplitudes, pointPaint); 
    } 
} 
+0

La tua soluzione è perfetta. Voglio solo aggiungere una cosa aggiuntiva a questo, ogni volta che l'utente fa clic sul pulsante di registrazione voglio aggiornare il visualizzatore e ridisegnarlo dall'inizio. Ho provato molte soluzioni ma non ha funzionato, puoi aiutarmi –

Problemi correlati