How to record and play the sound simultaneously (live stream over wireless or Bluetooth speaker) from your Android App? – Can be used for baby monitor
In this video, it shows how one can create a simple Android App to record and play the sound simultaneously.
The concept taken in this video is very simple. It first records the sound using the AudioRecord and plays the same sound using the AudioTrack in a while loop.
For transferring the data packets, it creates a local variable shortAudioData of type short. This variable is minimum buffer size for the ENCODING_PCM_16BIT encoding.
This kind of App can be used for monitoring your kids (baby monitor) at home or even spying. Say for example the kids are in one room and the adult can be another room with the speaker which continuously receive the sound of the kids room through this App.
As told in the video, this App has been hosted on the Play Store and can be referred using the below link: https://play.google.com/store/apps/details?id=com.programmerworld.babymonitor
I hope you liked this video. For any questions, suggestions or appreciations please contact us at: https://programmerworld.co/contact/ or email us at: programmerworld1990@gmail.com
Source Code:
package com.programmerworld.babymonitor;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView textViewStatus;
private EditText editTextGainFactor;
private AudioRecord audioRecord;
private AudioTrack audioTrack;
private int intBufferSize;
private short[] shortAudioData;
private int intGain;
private boolean isActive = false;
private Thread thread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, PackageManager.PERMISSION_GRANTED);
textViewStatus = findViewById(R.id.textViewStatus);
editTextGainFactor = findViewById(R.id.editTextGainFactor);
thread = new Thread(new Runnable() {
@Override
public void run() {
threadLoop();
}
});
}
public void buttonStart(View view){
isActive = true;
intGain = Integer.parseInt(editTextGainFactor.getText().toString());
textViewStatus.setText("Active");
thread.start();
}
public void buttonStop(View view){
isActive = false;
audioTrack.stop();
audioRecord.stop();
textViewStatus.setText("Stopped");
}
private void threadLoop(){
int intRecordSampleRate = AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);
intBufferSize = AudioRecord.getMinBufferSize(intRecordSampleRate, AudioFormat.CHANNEL_IN_MONO
, AudioFormat.ENCODING_PCM_16BIT);
shortAudioData = new short[intBufferSize];
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC
, intRecordSampleRate
, AudioFormat.CHANNEL_IN_STEREO
, AudioFormat.ENCODING_PCM_16BIT
, intBufferSize);
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC
, intRecordSampleRate
, AudioFormat.CHANNEL_IN_STEREO
, AudioFormat.ENCODING_PCM_16BIT
, intBufferSize
, AudioTrack.MODE_STREAM);
audioTrack.setPlaybackRate(intRecordSampleRate);
audioRecord.startRecording();
audioTrack.play();
while (isActive){
audioRecord.read(shortAudioData, 0, shortAudioData.length);
for (int i = 0; i< shortAudioData.length; i++){
shortAudioData[i] = (short) Math.min (shortAudioData[i] * intGain, Short.MAX_VALUE);
}
audioTrack.write(shortAudioData, 0, shortAudioData.length);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.programmerworld.babymonitor">
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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=".MainActivity">
<TextView
android:id="@+id/textViewStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="173dp"
android:layout_marginTop="65dp"
android:text="@string/status"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button2" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="148dp"
android:layout_marginTop="24dp"
android:onClick="buttonStart"
android:text="@string/start"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editTextGainFactor" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="148dp"
android:layout_marginTop="46dp"
android:onClick="buttonStop"
android:text="@string/stop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button" />
<EditText
android:id="@+id/editTextGainFactor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="89dp"
android:layout_marginTop="59dp"
android:ems="10"
android:hint="@string/enter_gain_factor"
android:inputType="number"
android:textAlignment="center"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:autofillHints="" />
</androidx.constraintlayout.widget.ConstraintLayout>
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.programmerworld.babymonitor"
minSdkVersion 24
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
Why can u start thread without exception, When i try start thread again after i click button, my app is crashing and i know that It is never legal to start a thread more than once.
Of course, main thread can not be started again. Once the App crashes it kills the main thread and the App needs to be restarted to start the thread.
So, for a robust programming, implement as much try-catch as possible to handle the exception elegantly.
Cheers
Programmer World
–