How to adjust microphone sensitivity while recordi

2019-01-11 12:08发布

问题:

I am working on a voice recording app. in app I have a Seekbar to change input voice gain. I could not found any way to adjust input voice gain.

I am using AudioRecord class to record voice.

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

    recorder.startRecording();

I have seen an app also in play store which are using this functionality.

https://play.google.com/store/apps/details?id=com.grace.microphone

回答1:

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.



回答2:

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.



回答3:

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);


回答4:

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);
    }