2

I am writing a Android program that Streams MIC directly to Speaker of Phone.The code works but UI hangs and app hangs.But Still audio transfer is working even if the app hangs.Where is the error..?

 RecordBufferSize=AudioRecord.getMinBufferSize(sampleRateInHz,AudioFormat.CHANNEL_IN_MONO,AudioFormat.ENCODING_PCM_16BIT);
    TrackBufferSize= AudioTrack.getMinBufferSize(sampleRateInHz,AudioFormat.CHANNEL_OUT_MONO,AudioFormat.ENCODING_PCM_16BIT);

    am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    Record record = new Record();
    record.run();

}
public class Record extends Thread
{

    final short[] buffer = new short[RecordBufferSize];
    short[] readBuffer = new short[TrackBufferSize];

    public void run() {
        isRecording = true;
        android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
        AudioRecord arec = new AudioRecord(MediaRecorder.AudioSource.MIC,sampleRateInHz,AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT,RecordBufferSize);
        AudioTrack atrack = new AudioTrack(AudioManager.STREAM_MUSIC,sampleRateInHz,AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, TrackBufferSize, AudioTrack.MODE_STREAM);
        //am.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_EARPIECE, AudioManager.ROUTE_ALL);
        atrack.setPlaybackRate(sampleRateInHz);
        byte[] buffer = new byte[RecordBufferSize];
        arec.startRecording();
        atrack.play();
        while(isRecording) {
            AudioLenght=arec.read(buffer, 0, RecordBufferSize);
            atrack.write(buffer, 0, AudioLenght);
        }
        arec.stop();
        atrack.stop();
        isRecording = false;
    }
}

This is my code.

anoopknr
  • 3,177
  • 2
  • 23
  • 33
  • Why do you want to do so? Will the MIC catch the sound from the speaker? It will cause serious sound feedbacks. – Raptor Mar 16 '18 at 06:22
  • I am working in an app for Hearing impaired people. so that surrouding audio can be amplified and heared throw headphones. They can avoid hearing aid to an extent. – anoopknr Mar 16 '18 at 13:22
  • Consider to run the thread in background: https://stackoverflow.com/questions/15472383/how-can-i-run-code-on-a-background-thread-on-android – Raptor Mar 19 '18 at 01:23

1 Answers1

4

I tried this and got result.Try this

Java Code I used:-

package com.example.root.akuvo;

import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.media.AudioAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.media.audiofx.AcousticEchoCanceler;
import android.media.audiofx.AutomaticGainControl;
import android.media.audiofx.BassBoost;
import android.media.audiofx.NoiseSuppressor;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;


public class MicToSpeakerActivity extends AppCompatActivity {

    //Audio
    private Button mOn;
    private boolean isOn;
    private boolean isRecording;
    private AudioRecord record;
    private AudioTrack player;
    private AudioManager manager;
    private int recordState, playerState;
    private int minBuffer;

    //Audio Settings
    private final int source = MediaRecorder.AudioSource.CAMCORDER;
    private final int channel_in = AudioFormat.CHANNEL_IN_MONO;
    private final int channel_out = AudioFormat.CHANNEL_OUT_MONO;
    private final int format = AudioFormat.ENCODING_PCM_16BIT;

    private final static int REQUEST_ENABLE_BT = 1;
    private boolean IS_HEADPHONE_AVAILBLE=false;

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mic_to_speaker);

        //Reduce latancy
        setVolumeControlStream(AudioManager.MODE_IN_COMMUNICATION);


        mOn = (Button) findViewById(R.id.button);
        isOn = false;
        isRecording = false;

        manager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
        manager.setMode(AudioManager.MODE_IN_COMMUNICATION);

        //Check for headset availability
        AudioDeviceInfo[] audioDevices = manager.getDevices(AudioManager.GET_DEVICES_ALL);
        for(AudioDeviceInfo deviceInfo : audioDevices) {
            if (deviceInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADPHONES || deviceInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET || deviceInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET) {
                IS_HEADPHONE_AVAILBLE = true;
            }
        }
        if (!IS_HEADPHONE_AVAILBLE){
            // get delete_audio_dialog.xml view

            LayoutInflater layoutInflater = LayoutInflater.from(MicToSpeakerActivity.this);
            View promptView = layoutInflater.inflate(R.layout.insert_headphone_dialog, null);
            AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(MicToSpeakerActivity.this);
            alertDialogBuilder.setView(promptView);

            // setup a dialog window
            alertDialogBuilder.setCancelable(false)
                    .setPositiveButton("Try Again", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            startActivity(new Intent(getIntent()));
                        }
                    })
                    .setNegativeButton("Cancel",
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int id) {
                                    startActivity(new Intent(MicToSpeakerActivity.this,MainActivity.class));
                                    dialog.cancel();
                                }
                            });

            // create an alert dialog
            AlertDialog alert = alertDialogBuilder.create();
            alert.show();
        }

        initAudio();

        mOn.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                mOn.getBackground().setColorFilter(getResources().getColor(!isOn ? R.color.colorOn : R.color.colorOff), PorterDuff.Mode.SRC_ATOP);
                isOn = !isOn;
                if(isOn) {
                    (new Thread() {
                        @Override
                        public void run()
                        {
                            startAudio();
                        }
                    }).start();
                } else {
                    endAudio();
                }
            }
        });
    }
    public void initAudio() {
        //Tests all sample rates before selecting one that works
        int sample_rate = getSampleRate();
        minBuffer = AudioRecord.getMinBufferSize(sample_rate, channel_in, format);

        record = new AudioRecord(source, sample_rate, channel_in, format, minBuffer);
        recordState = record.getState();
        int id = record.getAudioSessionId();
        Log.d("Record", "ID: " + id);
        playerState = 0;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            player = new AudioTrack(
                    new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build(),
                    new AudioFormat.Builder().setEncoding(format).setSampleRate(sample_rate).setChannelMask(channel_out).build(),
                    minBuffer,
                    AudioTrack.MODE_STREAM,
                    AudioManager.AUDIO_SESSION_ID_GENERATE);
            playerState = player.getState();
            // Formatting Audio
            if(AcousticEchoCanceler.isAvailable()) {
                AcousticEchoCanceler echo = AcousticEchoCanceler.create(id);
                echo.setEnabled(true);
                Log.d("Echo", "Off");
            }
            if(NoiseSuppressor.isAvailable()) {
                NoiseSuppressor noise = NoiseSuppressor.create(id);
                noise.setEnabled(true);
                Log.d("Noise", "Off");
            }
            if(AutomaticGainControl.isAvailable()) {
                AutomaticGainControl gain = AutomaticGainControl.create(id);
                gain.setEnabled(false);
                Log.d("Gain", "Off");
            }
            BassBoost base = new BassBoost(1, player.getAudioSessionId());
            base.setStrength((short) 1000);
        }
    }

    public void startAudio() {
        int read = 0, write = 0;
        if(recordState == AudioRecord.STATE_INITIALIZED && playerState == AudioTrack.STATE_INITIALIZED) {
            record.startRecording();
            player.play();
            isRecording = true;
            Log.d("Record", "Recording...");
        }
        while(isRecording) {
            short[] audioData = new short[minBuffer];
            if(record != null)
                read = record.read(audioData, 0, minBuffer);
            else
                break;
            Log.d("Record", "Read: " + read);
            if(player != null)
                write = player.write(audioData, 0, read);
            else
                break;
            Log.d("Record", "Write: " + write);
        }
    }

    public void endAudio() {
        if(record != null) {
            if(record.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING)
                record.stop();
            isRecording = false;
            Log.d("Record", "Stopping...");
        }
        if(player != null) {
            if(player.getPlayState() == AudioTrack.PLAYSTATE_PLAYING)
                player.stop();
            isRecording = false;
            Log.d("Player", "Stopping...");
        }
    }

    public int getSampleRate() {
        //Find a sample rate that works with the device
        for (int rate : new int[] {8000, 11025, 16000,  22050, 44100, 48000}) {
            int buffer = AudioRecord.getMinBufferSize(rate, channel_in, format);
            if (buffer > 0)
                return rate;
        }
        return -1;
    }

}

XML Code I Used :

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.root.akuvo.MicToSpeakerActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="104dp"
        android:layout_height="102dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:background="@android:drawable/ic_lock_power_off"
        android:backgroundTint="@color/colorOff"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.396" />

</android.support.constraint.ConstraintLayout>
anoopknr
  • 3,177
  • 2
  • 23
  • 33