Android: аудиозапись с визуализацией уровня голоса

Мне нужно создать приложение для Android, которое предназначено для записи голоса при показе визуализации уровня голоса (звука).

Я уже создал приложение для записи звука, но я не могу добавить к нему визуализацию уровня звука. Как мне это сделать?

Пожалуйста, кто-нибудь поможет мне дать предложение или ссылку на образец учебника или код.

Создайте 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

Когда он достигает конца, анимация продолжается, как ожидалось: стирание начала графика.

Если вы используете класс MediaRecorder и визуализация на основе амплитуды пиков в порядке, вы можете использовать метод getMaxAmplitude () для непрерывного опроса для «максимальной абсолютной амплитуды, которая была выбрана с момента последнего вызова».
Масштабируйте эту амплитуду в индекс, который определяет, сколько графических томов вашего приложения загорается, и вы настроены.

Проверьте эту ссылку: http://code.google.com/p/android-labs/source/browse/trunk/NoiseAlert/src/com/google/android/noisealert/SoundMeter.java.

Он записывает аудио в / dev / null и проверяет громкость. Но вы можете изменить имя файла, чтобы сохранить записанный звук.

Мне нравится ответ Али, но вот более простая версия, которая работает намного лучше.

Я запустил RecordingActivity и установил его на весь экран, но вы можете создать ресурс макета или добавить представление в любом месте.

RecordingActivity с полноэкранным просмотром

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