2013-01-12 46 views
9

私は音声(音)レベルの視覚化を表示しながら音声を録音するためのアンドロイドアプリケーションを作成する必要があります。Android:音声レベルの視覚化による音声の録音

私はすでにオーディオレコーディングアプリケーションを作成しましたが、サウンドレベルの視覚化を追加することはできません。どうしたらいいですか?

私に寄付の提案やサンプルのチュートリアルのリンクやコードを教えてください。

答えて

2

MediaRecorderクラスを使用しており、ピーク振幅に基づくビジュアライゼーションがOKの場合は、getMaxAmplitude()メソッドを使用して、「最後の呼び出し以降にサンプリングされた絶対最大振幅」を連続的にポーリングできます。
あなたのアプリのグラフィカルボリュームバーのどれだけが点灯し、設定されているかを決定するインデックスに振幅をスケールダウンします。

+1

サンプルコードはありますか? – user1972690

+1

サンプルコードはありません。しかし、そのほとんどは、簡単なAndroid UIのコーディングのように思えるでしょう。 [あなたは定期的に実行する](ハンドラ)を開始します(http://stackoverflow.com/questions/6242268/repeat-a-task-with-a-time-delay/6242292#6242292)、 'getMaxAmplitude'メソッドを呼び出します。あなたの 'MediaRecorder'は現在のピークレベルを取得し、新しいピークレベルに基づいてボリュームバーを含む' View'を更新し、最後に[postInvalidate]を実行します(http://stackoverflow.com/questions/5521596/what- does-postinvalidate-do)を使用して、変更されたビューが再描画されるように要求します。 – Michael

+0

@Michaelこれは非常に意味があるようですが、Visualizerの初期化にはどのaudioSession IDを使用するのか不明です。 –

28

このようなxml activity_recording.xmlを作成します。

<?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> 

以下のようにカスタムビジュアライザビューを作成します。

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); 
     } 
    } 

} 

下記のようにRecordingActivityクラスを作成します。

パッケージ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); 
      } 
     } 
    }; 


} 

結果

これは、それがどのように見えるかです:予想通り https://www.youtube.com/watch?v=BoFG6S02GH0

それが最後に到達する、アニメーションは継続:グラフの始まりを消去します。

+2

許可を追加することを忘れないでください。使用許諾android:name = "android.permission.RECORD_AUDIO" /> Ali

+1

あなたは、またはあなたのソリューションを使用している誰かが、動作中の例のスクリーンショットを追加しました – vedant1811

+0

これは動作しません。録音を開始するときに録音を開始しないでください。停止するときに例外があります。私を助けてください。例外は以下の通りです –

3

私はアリの答えが好きですが、ここでははるかに優れた簡単なバージョンです。

RecordingActivityを起動してフルスクリーンに設定しましたが、レイアウトリソースを作成したり、ビューをどこにでも追加することができます。

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); 
    } 
} 

ビュークラスのonDrawメソッドは、可能な限り高速である必要があります。

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

あなたのソリューションは完璧です。私はちょうどこれを追加するだけで、ユーザーがレコードボタンをクリックするたびにビジュアライザをリフレッシュし、最初から再描画したいと思います。私は多くの解決策を試しましたが、うまくいかなかった、あなたが私を助けてくれますか? –