Нарисуйте аудио данные, захваченные в главном виде

Я пытаюсь нарисовать аудиоданные, записанные на главном экране, например осциллограф. Я использую AudioRecord.OnRecordPositionUpdateListener(), но моя программа рисует только один раз.

программа вызывает AudioRecord.OnRecordPositionUpdateListener() непрерывно, но больше не рисует после того, как рисует аудиоданные только один раз;

Я хочу рисовать это постоянно. Помогите мне, пожалуйста. Я оставляю исходный код

Основной класс деятельности

public class MainActivity extends Activity {
    Button btnTime  = null;
    Button btnVolt  = null;
    Button btnUp    = null;
    Button btnDown  = null;

    int nScreenX;
    int nScreenY;
    int nWRatio = 7;
    int nHRatio = 12;

    int nScopeX;
    int nScopeY;

    ImageView   drawingImageView;
    Canvas      scopeCanvas;
    Paint scopePaint = new Paint();

    AudioRecord audioRecord = null;
    static final int SAMPLE_RATE = 44100;
    short[] buffer = new short[44100];

    int nTimeDiv = 100;                        // unit : 10us/Div
    static final int nResolution = 200;         // Screen Resolution
    int nImgBuffPos = 0;
    short[] imgBuff = new short[nResolution];

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

        DisplayMetrics metrics = this.getResources().getDisplayMetrics();
        Display display = getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);
        nScreenX = size.x;
        nScreenY = size.y;

        initiateButton();
        initiateImageview();
        initiateAudioCapture();
    }

    private void initiateButton() {
        btnTime = (Button) findViewById(R.id.button_time);
        btnVolt = (Button) findViewById(R.id.button_volt);
        btnUp   = (Button) findViewById(R.id.button_up);
        btnDown = (Button) findViewById(R.id.button_down);

        // 버튼 폭 설정
        btnTime.setWidth(nScreenX/nWRatio);
        btnVolt.setWidth(nScreenX / nWRatio);
        btnUp.setWidth(nScreenX/nWRatio);
        btnDown.setWidth(nScreenX / nWRatio);

        // 버튼 높이 설정
        btnTime.setHeight(nScreenY / nHRatio);
        btnVolt.setHeight(nScreenY / nHRatio);
        btnUp.setHeight(nScreenY/nHRatio);
        btnDown.setHeight(nScreenY / nHRatio);

        // 버튼 X 좌표 설정
        btnTime.setX(0);
        btnVolt.setX(0);
        btnUp.setX(0);
        btnDown.setX(0);

        // 버튼 Y 좌표 설정
        btnTime.setY(0);
        btnVolt.setY(nScreenY/(nHRatio-4));
        btnUp.setY(nScreenY*2/(nHRatio-4));
        btnDown.setY(nScreenY*3/(nHRatio-4));

        nScopeX = nScreenX - nScreenX/nWRatio;
        nScopeY = nScreenY - nScreenY/nHRatio;
    }

    private void initiateImageview() {
        drawingImageView = (ImageView) this.findViewById(R.id.DrawingImageView);

        Bitmap bitmap = Bitmap.createBitmap((int) nScopeX, nScopeY, Bitmap.Config.ARGB_8888);

        drawingImageView.setBackgroundColor(0xff000000);

        RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) drawingImageView.getLayoutParams();
        params.leftMargin = nScreenX/nWRatio;
        params.topMargin = 0;
        drawingImageView.setLayoutParams(params);

        scopeCanvas = new Canvas(bitmap);
        drawingImageView.setImageBitmap(bitmap);

        backgroudGrid();
    }

    private void backgroudGrid() {
        scopePaint.setColor(Color.rgb(150, 150, 150));
        scopePaint.setStrokeWidth(6);

        for(int i=1; i<10; i++) {
            if(i != 4 && i < 8) {
                scopeCanvas.drawLine(0, nScopeY * i / 8, nScopeX, nScopeY * i / 8, scopePaint);
            }
            if(i != 5) {
                scopeCanvas.drawLine(nScopeX * i / 10, 0, nScopeX * i / 10, nScopeY, scopePaint);
            }
        }

        scopePaint.setColor(Color.rgb(255, 255, 255));
        scopePaint.setStrokeWidth(10);

        // set Outline
        scopeCanvas.drawLine(0, nScopeY/2, nScopeX, nScopeY/2, scopePaint);
        scopeCanvas.drawLine(nScopeX/2, 0, nScopeX/2, nScopeY, scopePaint);
        scopeCanvas.drawLine(0, 0, 0, nScopeY, scopePaint);
        scopeCanvas.drawLine(nScopeX, nScopeY, 0, nScopeY, scopePaint);
        scopeCanvas.drawLine(0, 0, nScopeX, 0, scopePaint);
        scopeCanvas.drawLine(nScopeX, 0, nScopeX, nScopeY, scopePaint);
    }

    private void initiateAudioCapture() {
        int min = AudioRecord.getMinBufferSize(SAMPLE_RATE,
                AudioFormat.CHANNEL_IN_MONO,
                AudioFormat.ENCODING_PCM_16BIT);
        audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
                SAMPLE_RATE,
                AudioFormat.CHANNEL_IN_MONO,
                AudioFormat.ENCODING_PCM_16BIT,
                min);
        audioRecord.setRecordPositionUpdateListener(updateListener);
        audioRecord.setPositionNotificationPeriod(200);
        audioRecord.startRecording();
    }

    public void drawScope() {
        int sx=0, ex=0, sy=0, ey=0;
        if(scopeCanvas == null)
            return;

        scopeCanvas.drawColor(Color.BLACK);

        backgroudGrid();

        scopePaint.setColor(0xff00ffff);
        scopePaint.setStrokeWidth(3);

        for(int i=0; i<nResolution-1; i++) {
            sx = scopeCanvas.getWidth()/200*i;
            ex = scopeCanvas.getWidth()/200*(i+1);
            sy = scopeCanvas.getHeight()/2 + imgBuff[i];
            ey = scopeCanvas.getHeight()/2 + imgBuff[i+1];
            scopeCanvas.drawLine(sx, sy, ex, ey, scopePaint);
        }
    }

    private AudioRecord.OnRecordPositionUpdateListener updateListener = new AudioRecord.OnRecordPositionUpdateListener() {
        public void onPeriodicNotification(AudioRecord recorder) {
            int len = recorder.read(buffer, 0, buffer.length);

            float rate = ((float) SAMPLE_RATE)/nResolution/100;

            int pos=0;
            for(int i=0; i<len; i++) {
                pos = (int)(i*rate);
                if(pos < len) {
                    imgBuff[nImgBuffPos++] = buffer[pos];
                    if (nImgBuffPos >= nResolution) {
                        drawScope();
                        nImgBuffPos = 0;
                    }
                }
            }
        }

        public void onMarkerReached(AudioRecord recorder){}
    };

}

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TIME"
        android:id="@+id/button_time"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="VOLT"
        android:id="@+id/button_volt"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="UP"
        android:id="@+id/button_up"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="DOWN"
        android:id="@+id/button_down"/>

    <ImageView
        android:id="@+id/DrawingImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mycompany.oscilloscope" >

    <uses-permission android:name="android.permission.RECORD_AUDIO"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:screenOrientation="landscape"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

1 ответ

Есть несколько проблем с вашим проектом.

Прежде всего: он не рисует непрерывно, потому что вы перерисовываете растровое изображение в drawScope(), но никогда не сообщаете ImageView, что его содержимое было изменено и его необходимо перекрасить. Для этого в конце drawScope() добавьте:

drawingImageView.invalidate();

После этого вы не увидите хорошей перерисовки, потому что вызовы onPeriodicNotification() выполняются слишком быстро. Невозможно завершить предыдущие перерисовки, пока появляются новые уведомления. Вы можете увеличить срок уведомления. Для этого вам нужно увеличить входной буфер достаточно. Лучше не использовать его в минимальном размере:

private void initiateAudioCapture() {
    int min = AudioRecord.getMinBufferSize(SAMPLE_RATE,
            AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT);
    if (min < 16384) min = 16384;
    audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
            SAMPLE_RATE,
            AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT,
            min);
    audioRecord.setRecordPositionUpdateListener(updateListener);
    audioRecord.setPositionNotificationPeriod(4000);
    audioRecord.startRecording();
}

Теперь, в зависимости от скорости обработки вашего устройства, вы можете увидеть изменяющееся изображение.

Я предлагаю вам попробовать другой подход: 1) Вместо того, чтобы перерисовывать растровое изображение и рисовать его в ImageView, создайте пользовательский вид и нарисуйте все, что вам не нужно, внутри его метода onDraw(). 2) Откажитесь от использования RecordPositionUpdateListener, вместо этого используйте таймер для периодического вызова обновления. Таким образом, если ваша перекраска будет отложена, некоторые входные данные будут потеряны, но все будет в порядке.

Другой подход заключается в сборе записанных данных в отдельном потоке.

Другие вопросы по тегам