Increase volume output of recorded audio
Asked Answered
H

4

12

I am trying to make a call recording app in Android. I am using loudspeaker to record both uplink and downlink audio. The only problem I am facing is the volume is too low. I've increased the volume of device using AudioManager to max and it can't go beyond that.

I've first used MediaRecorder, but since it had limited functions and provides compressed audio, I've tried with AudioRecorder. Still I havn't figured out how to increase the audio. I've checked on projects on Github too, but it's of no use. I've searched on stackoverflow for last two weeks, but couldn't find anything at all.

I am quite sure that it's possible, since many other apps are doing it. For instance Automatic Call recorder does that.

I understand that I have to do something with the audio buffer, but I am not quite sure what needs to be done on that. Can you guide me on that.

Update:-
I am sorry that I forgot to mention that I am already using Gain. My code is almost similar to RehearsalAssistant (in fact I derived it from there). The gain doesn't work for more than 10dB and that doesn't increase the audio volume too much. What I wanted is I should be able to listen to the audio without putting my ear on the speaker which is what lacking in my code.

I've asked a similar question on functioning of the volume/loudness at SoundDesign SE here. It mentions that the Gain and loudness is related but it doesn't set the actual loudness level. I am not sure how things work, but I am determined to get the loud volume output.

Hygeia answered 28/9, 2014 at 18:54 Comment(0)
C
16

You obviously have the AudioRecord stuff running, so I skip the decision for sampleRate and inputSource. The main point is that you need to appropriately manipulate each sample of your recorded data in your recording loop to increase the volume. Like so:

    int minRecBufBytes = AudioRecord.getMinBufferSize( sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT );
    // ...
    audioRecord = new AudioRecord( inputSource, sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, minRecBufBytes );

    // Setup the recording buffer, size, and pointer (in this case quadruple buffering)
    int recBufferByteSize = minRecBufBytes*2;
    byte[] recBuffer = new byte[recBufferByteSize];
    int frameByteSize = minRecBufBytes/2;
    int sampleBytes = frameByteSize;
    int recBufferBytePtr = 0;

    audioRecord.startRecording();

    // Do the following in the loop you prefer, e.g.
    while ( continueRecording ) {
        int reallySampledBytes = audioRecord.read( recBuffer, recBufferBytePtr, sampleBytes );

        int i = 0;
        while ( i < reallySampledBytes ) {
            float sample = (float)( recBuffer[recBufferBytePtr+i  ] & 0xFF
                                  | recBuffer[recBufferBytePtr+i+1] << 8 );

            // THIS is the point were the work is done:
            // Increase level by about 6dB:
            sample *= 2;
            // Or increase level by 20dB:
            // sample *= 10;
            // Or if you prefer any dB value, then calculate the gain factor outside the loop
            // float gainFactor = (float)Math.pow( 10., dB / 20. );    // dB to gain factor
            // sample *= gainFactor;

            // Avoid 16-bit-integer overflow when writing back the manipulated data:
            if ( sample >= 32767f ) {
                recBuffer[recBufferBytePtr+i  ] = (byte)0xFF;
                recBuffer[recBufferBytePtr+i+1] =       0x7F;
            } else if ( sample <= -32768f ) {
                recBuffer[recBufferBytePtr+i  ] =       0x00;
                recBuffer[recBufferBytePtr+i+1] = (byte)0x80;
            } else {
                int s = (int)( 0.5f + sample );  // Here, dithering would be more appropriate
                recBuffer[recBufferBytePtr+i  ] = (byte)(s & 0xFF);
                recBuffer[recBufferBytePtr+i+1] = (byte)(s >> 8 & 0xFF);
            }
            i += 2;
        }

        // Do other stuff like saving the part of buffer to a file
        // if ( reallySampledBytes > 0 ) { ... save recBuffer+recBufferBytePtr, length: reallySampledBytes

        // Then move the recording pointer to the next position in the recording buffer
        recBufferBytePtr += reallySampledBytes;

        // Wrap around at the end of the recording buffer, e.g. like so:
        if ( recBufferBytePtr >= recBufferByteSize ) {
            recBufferBytePtr = 0;
            sampleBytes = frameByteSize;
        } else {
            sampleBytes = recBufferByteSize - recBufferBytePtr;
            if ( sampleBytes > frameByteSize )
                sampleBytes = frameByteSize;
        }
    }
Cowardly answered 3/10, 2014 at 8:42 Comment(10)
Actually I am already using the Gain, but it doesn't work for more than 10dB increase. I am using code from this URL - sourceforge.net/p/rehearsalassist/code/HEAD/tree/android/…Hygeia
You say it doesn't work for more than 10dB? What exactly do you observe at say 12 or 20dB?Cowardly
The sound gets distorted at more than 10dB gain. It starts to get a lot of noise and at 20 dB you can not identify the sound at all.Hygeia
Then, my code will definitely help you because it produces much less distortion. 18dB should be perfectly possible, of course you increase the background noise by the same amount, but since the speech is very loud then, it masks the background noise and should be perfectly understandably. Of course, you can further increase it, but then it becomes really tricky.Cowardly
Thanks, it did worked at near 12-14 dB. I did merged the code from the sonic library too to increase volume, but that increase too much noise and distortion, so I kept the volume at 1.5-2.0 and instead tried to increase gain. I got decent sound volume which doesn't sound too loud in phone, but when listened on a PC sounds loud enough. Looks like that's the farthest I could go.Hygeia
@Hartmut Pfitzinger How to apply gain in Stereo recording mode i.e. AudioFormat.CHANNEL_IN_STEREO?Mendelism
@Mendelism Simply do that twice, separately for the data of each channel but with the same gain value.Cowardly
I am using the above code, but the file which I am creating using this is not playable. I am getting E/MediaPlayer: Error (1,-2147483648) any suggestion why I might be getting this.Perzan
What if i want to store the audio in the file? I am not getting whether to store the samplebytes or recbuffer in the file. Please help.Anatolic
@Anatolic Write fileOutputStream.write(recBuffer, 0, recBuffer.size) just after the last if-else block (inside while loop). I know it's too late to help your problem, but hope to help others.Thrombus
H
3

Thanks to Hartmut and beworker for the solution. Hartmut's code did worked at near 12-14 dB. I did merged the code from the sonic library too to increase volume, but that increase too much noise and distortion, so I kept the volume at 1.5-2.0 and instead tried to increase gain. I got decent sound volume which doesn't sound too loud in phone, but when listened on a PC sounds loud enough. Looks like that's the farthest I could go.

I am posting my final code to increase the loudness. Be aware that using increasing mVolume increases too much noise. Try to increase gain instead.

private AudioRecord.OnRecordPositionUpdateListener updateListener = new AudioRecord.OnRecordPositionUpdateListener() {
        @Override
        public void onPeriodicNotification(AudioRecord recorder) {
            aRecorder.read(bBuffer, bBuffer.capacity()); // Fill buffer
            if (getState() != State.RECORDING)
                return;
            try {
                if (bSamples == 16) {
                    shBuffer.rewind();
                    int bLength = shBuffer.capacity(); // Faster than accessing buffer.capacity each time
                    for (int i = 0; i < bLength; i++) { // 16bit sample size
                        short curSample = (short) (shBuffer.get(i) * gain);
                        if (curSample > cAmplitude) { // Check amplitude
                            cAmplitude = curSample;
                        }
                        if(mVolume != 1.0f) {
                            // Adjust output volume.
                            int fixedPointVolume = (int)(mVolume*4096.0f);
                            int value = (curSample*fixedPointVolume) >> 12;
                            if(value > 32767) {
                                value = 32767;
                            } else if(value < -32767) {
                                value = -32767;
                            }
                            curSample = (short)value;
                            /*scaleSamples(outputBuffer, originalNumOutputSamples, numOutputSamples - originalNumOutputSamples,
                                    mVolume, nChannels);*/
                        }
                        shBuffer.put(curSample);
                    }
                } else { // 8bit sample size
                    int bLength = bBuffer.capacity(); // Faster than accessing buffer.capacity each time
                    bBuffer.rewind();
                    for (int i = 0; i < bLength; i++) {
                        byte curSample = (byte) (bBuffer.get(i) * gain);
                        if (curSample > cAmplitude) { // Check amplitude
                            cAmplitude = curSample;
                        }
                        bBuffer.put(curSample);
                    }
                }
                bBuffer.rewind();
                fChannel.write(bBuffer); // Write buffer to file
                payloadSize += bBuffer.capacity();
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(NoobAudioRecorder.class.getName(), "Error occured in updateListener, recording is aborted");
                stop();
            }
        }

        @Override
        public void onMarkerReached(AudioRecord recorder) {
            // NOT USED
        }
    };
Hygeia answered 9/10, 2014 at 7:32 Comment(1)
Hi, what is mVolume how did you get this value?Electrophilic
K
2

simple use MPEG_4 format

To increase the call recording volume use AudioManager as follows:

int deviceCallVol;
AudioManager audioManager;

Start Recording:

   audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
//get the current volume set
deviceCallVol = audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
//set volume to maximum
        audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL), 0);

   recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL);
   recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
   recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
   recorder.setAudioEncodingBitRate(32);
   recorder.setAudioSamplingRate(44100);

Stop Recording:

//revert volume to initial state

 audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, deviceCallVol, 0);
Kakapo answered 2/10, 2014 at 12:58 Comment(2)
Thanks for the answer. I've already tried various formats. MediaRecorder definitely doesn't solve my purpose(Except if there's a hack or work around). I am sort of expecting some kind of algorithm which can be applied to audio buffer to improve loudness.Hygeia
Thanks for this answer. I had similar issues, and changing MPEG_4/AAC from 3GPP/AMR_NB improved quality and volume noticeably.Zaller
J
1

In my app I use an open source sonic library. Its main purpose is to speed up / slow down speech, but besides this it allows to increase loudness too. I apply it to playback, but it must work for recording similarly. Just pass your samples through it before compressing them. It has a Java interface too. Hope this helps.

Jazzman answered 6/10, 2014 at 9:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.