This repository has been archived by the owner on May 10, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
130 additions
and
0 deletions.
There are no files selected for viewing
8 changes: 8 additions & 0 deletions
8
MembraneRTC/src/main/java/org/membraneframework/rtc/media/OnSoundVolumeChangedListener.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package org.membraneframework.rtc.media; | ||
|
||
import org.membraneframework.rtc.models.VadStatus; | ||
|
||
@FunctionalInterface | ||
public interface OnSoundVolumeChangedListener { | ||
void onSoundVolumeChangedListener(VadStatus value); | ||
} |
122 changes: 122 additions & 0 deletions
122
MembraneRTC/src/main/java/org/membraneframework/rtc/media/SoundVolumeMonitor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package org.membraneframework.rtc.media; | ||
|
||
|
||
import android.media.AudioFormat; | ||
import android.media.AudioRecord; | ||
import android.media.MediaRecorder; | ||
|
||
import org.membraneframework.rtc.models.VadStatus; | ||
|
||
import java.util.Timer; | ||
import java.util.TimerTask; | ||
|
||
import timber.log.Timber; | ||
|
||
public class SoundVolumeMonitor { | ||
private Timer timer; | ||
private AudioRecord audioRecord; | ||
private OnSoundVolumeChangedListener onSoundVolumeChangedListener; | ||
public boolean isRecording = false; | ||
private int bufferSize; | ||
private final int volumeThreshold = -100; | ||
private final int monitorInterval = 1; | ||
private final int samplingRate = 22050; | ||
|
||
public void start() throws SecurityException { | ||
start(monitorInterval, samplingRate); | ||
} | ||
|
||
public void start(int monitorInterval, int samplingRate) throws SecurityException { | ||
if (isRecording) return; | ||
|
||
bufferSize = AudioRecord.getMinBufferSize(samplingRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); | ||
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, samplingRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize); | ||
|
||
if (audioRecord.getState() == AudioRecord.STATE_INITIALIZED) { | ||
audioRecord.startRecording(); | ||
isRecording = true; | ||
startTimer(monitorInterval); | ||
} else { | ||
Timber.e("COULDNT_PREPARE_RECORDING AudioRecord couldn't be initialized."); | ||
} | ||
} | ||
|
||
public void stop() { | ||
if (!isRecording) { | ||
Timber.e("INVALID_STATE Please call start before stopping recording"); | ||
return; | ||
} | ||
stopTimer(); | ||
isRecording = false; | ||
try { | ||
audioRecord.stop(); | ||
audioRecord.release(); | ||
} catch (final RuntimeException e) { | ||
Timber.e("RUNTIME_EXCEPTION No valid audio data received. You may be using a device that can't record audio."); | ||
} finally { | ||
audioRecord = null; | ||
} | ||
} | ||
|
||
public void setSoundVolumeListener() { | ||
} | ||
|
||
public void setSoundVolumeListener(OnSoundVolumeChangedListener listener) { | ||
onSoundVolumeChangedListener = listener; | ||
} | ||
public void setIsSoundDetected(VadStatus newValue) { | ||
onSoundVolumeChangedListener.onSoundVolumeChangedListener(newValue); | ||
} | ||
private void detectSound(int volumeValue){ | ||
detectSound(volumeThreshold,volumeValue); | ||
} | ||
private void detectSound(int volumeThreshold, int volumeValue) { | ||
if (volumeValue > volumeThreshold) { | ||
setIsSoundDetected(VadStatus.SPEECH); | ||
} else { | ||
setIsSoundDetected(VadStatus.SILENCE); | ||
} | ||
} | ||
|
||
private int getMaxAmplitude(short[] buffer, int bytesRead) { | ||
int maxAmplitude = 0; | ||
for (int sample : buffer) { | ||
maxAmplitude = Math.max(maxAmplitude, Math.abs(sample)); | ||
} | ||
return maxAmplitude; | ||
} | ||
|
||
private int calculateValue(int maxAmplitude) { | ||
if (maxAmplitude <= 0) { | ||
return -160; | ||
} | ||
return (int) (20 * Math.log(((double) maxAmplitude) / 32767d)); | ||
} | ||
|
||
private int readAudioData(short[] buffer) { | ||
return audioRecord.read(buffer, 0, bufferSize); | ||
} | ||
|
||
private void startTimer(int monitorInterval) { | ||
timer = new Timer(); | ||
timer.scheduleAtFixedRate(new TimerTask() { | ||
@Override | ||
public void run() { | ||
short[] buffer = new short[bufferSize]; | ||
int bytesRead = readAudioData(buffer); | ||
int amplitude = getMaxAmplitude(buffer, bytesRead); | ||
int value = calculateValue(amplitude); | ||
detectSound(value); | ||
} | ||
}, 0, monitorInterval); | ||
} | ||
|
||
private void stopTimer() { | ||
if (timer != null) { | ||
timer.cancel(); | ||
timer.purge(); | ||
timer = null; | ||
} | ||
} | ||
} | ||
|