10

I'm working on a voice recording app. In it, I have a Seekbar to change the input voice gain. I couldn't find any way to adjust the input voice gain.

I am using the AudioRecord class to record voice.

 recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
            RECORDER_SAMPLERATE, RECORDER_CHANNELS,
            RECORDER_AUDIO_ENCODING, bufferSize);

    recorder.startRecording();

I've seen an app in the Google Play Store using this functionality.

4 Answers4

22

As I understand you don't want any automatic adjustments, only manual from the UI. There is no built-in functionality for this in Android, instead you have to modify your data manually.

Suppose you use read (short[] audioData, int offsetInShorts, int sizeInShorts) for reading the stream. So you should just do something like this:

float gain = getGain(); // taken from the UI control, perhaps in range from 0.0 to 2.0
int numRead = read(audioData, 0, SIZE);
if (numRead > 0) {
    for (int i = 0; i < numRead; ++i) {
        audioData[i] = (short)Math.min((int)(audioData[i] * gain), (int)Short.MAX_VALUE);
    }
}

Math.min is used to prevent overflow if gain is greater than 1.

Anton Savin
  • 40,838
  • 8
  • 54
  • 90
  • thanks for help. i have done some modifications with your code and my problem is now solved. –  Aug 29 '14 at 06:34
  • 1
    you also need to call Math.max(intValue, Short.MIN_VALUE) since Java short is signed, see my answer below. – Dmide Jul 10 '17 at 16:40
  • Only this answer works after searching a lot on the net. I had to set `gain` to over 7 to see some great effect! Thanks! – Farmaker Dec 15 '19 at 10:05
3

Dynamic microphone sensitivity is not a thing that the hardware or operating system is capable of as it requires analysis on the recorded sound. You should implement your own algorithm to analyze the recorded sound and adjust (amplify or decrease) the sound level on your own.

You can start by analyzing last few seconds and find a multiplier that is going to "balance" the average amplitude. The multiplier must be inversely proportional to the average amplitude to balance it.

PS: If you still want to do it, the mic levels are accessible when you have a root access, but I am still not sure -and don't think it is possible- if you can change the settings while recording. Hint: "/system/etc/snd_soc_msm" file.

Seyf
  • 889
  • 8
  • 16
0

Solution by OP.

I have done it using

final int USHORT_MASK = (1 << 16) - 1;

final ByteBuffer buf = ByteBuffer.wrap(data).order(
                        ByteOrder.LITTLE_ENDIAN);
final ByteBuffer newBuf = ByteBuffer.allocate(
                        data.length).order(ByteOrder.LITTLE_ENDIAN);

int sample;
        while (buf.hasRemaining()) {
                sample = (int) buf.getShort() & USHORT_MASK;
                sample *= db_value_global;
                        newBuf.putShort((short) (sample & USHORT_MASK));
                }

                data = newBuf.array();

                os.write(data);
Cœur
  • 37,241
  • 25
  • 195
  • 267
-1

This is working implementation based on ByteBuffer for 16bit audio. It's important to clamp the increased value from both sides since short is signed. It's also important to set the native byte order to ByteBuffer since audioRecord.read() returns native endian bytes.

You may also want to perform audioRecord.read() and following code in a loop, calling data.clear() after each iteration.

    double gain = 2.0;

    ByteBuffer data = ByteBuffer.allocateDirect(SAMPLES_PER_FRAME).order(ByteOrder.nativeOrder());

    int audioInputLengthBytes = audioRecord.read(data, SAMPLES_PER_FRAME);
    ShortBuffer shortBuffer = data.asShortBuffer();
    for (int i = 0; i < audioInputLengthBytes / 2; i++) { // /2 because we need the length in shorts
        short s = shortBuffer.get(i);
        int increased = (int) (s * gain);
        s = (short) Math.min(Math.max(increased, Short.MIN_VALUE), Short.MAX_VALUE);
        shortBuffer.put(i, s);
    }
Dmide
  • 6,422
  • 3
  • 24
  • 31