Android: AudioTrack makes a click sound at beginning

妖精的绣舞 提交于 2021-02-07 20:59:21

问题


I'm working on an Android app and I would like to play some short sounds(~ 2s). I tried Soundpool but it doesn't really suit for me since it can't check if a sounds is already playing. So I decided to use AudioTrack.

It works quite good BUT most of the time, when it begins to play a sound there is a "click" sound. I checked my audiofiles and they are clean.

I use audiotrack on stream mode. I saw that static mode is better for short sounds but after many searchs I still don't understand how to make it work. I also read that the clicking noise can be caused by the header of the wav file, so maybe the sound would disappear if I skip this header with setPlaybackHeadPosition(int positionInFrames) function (that is supposed to work only in static mode)

Here is my code (so the problem is the ticking noise at the beginning)

    int minBufferSize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, 
            AudioFormat.ENCODING_PCM_16BIT);
    audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, 
         AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM); 
audioTrack.play();
            int i = 0;
            int bufferSize = 2048; //don't really know which value to put
            audioTrack.setPlaybackRate(88200);

            byte [] buffer = new byte[bufferSize];
//there we open the wav file >
            InputStream inputStream = getResources().openRawResource(R.raw.abordage);
            try {
                while((i = inputStream.read(buffer)) != -1)
                    audioTrack.write(buffer, 0, i);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            try {
                inputStream.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

Does anyone has a solution to avoid that noise? I tried this, that works sometimes but not everytime. Could someone show me how to implement audiotrack in MODE_STATIC ? Thank you


回答1:


A common cause of audio "pop" is due to the rendering process not starting/stopping sound at the zero cross over point (assuming min/max of -1 to +1 cross over would be 0). Transducers like speakers or ear-buds are at rest (no sound input) which maps to this zero cross over. If an audio rendering process fails to start/stop from/to this zero, the transducer is being asked to do the impossible, namely instantaneously go from its resting state to some non-zero position in its min/max movement range, (or visa versa if you get a "pop" at the end).




回答2:


Finally, after a lot of experimentation, I made it work without the click noise. Here is my code (unfortunaly, I can't read the size of the inputStream since the getChannel().size() method only works with FileInputStream type)

try{
    long totalAudioLen = 0;  
    InputStream inputStream = getResources().openRawResource(R.raw.abordage); // open the file
    totalAudioLen = inputStream.available();
    byte[] rawBytes = new byte[(int)totalAudioLen];
    AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC,
                                      44100,
                                      AudioFormat.CHANNEL_CONFIGURATION_MONO,
                                      AudioFormat.ENCODING_PCM_16BIT,
                                     (int)totalAudioLen,
                                     AudioTrack.MODE_STATIC);
    int offset = 0;
    int numRead = 0;

    track.setPlaybackHeadPosition(100); // IMPORTANT to skip the click
    while (offset < rawBytes.length
               && (numRead=inputStream.read(rawBytes, offset, rawBytes.length-offset)) >= 0) {
            offset += numRead;
    } //don't really know why it works, it reads the file
    track.write(rawBytes, 0, (int)totalAudioLen); //write it in the buffer?
    track.play();  // launch the play
    track.setPlaybackRate(88200);
    inputStream.close();
    }
    catch (FileNotFoundException e) {

        Log.e(TAG, "Error loading audio to bytes", e);
    } catch (IOException e) {
        Log.e(TAG, "Error loading audio to bytes", e);
    } catch (IllegalArgumentException e) {
        Log.e(TAG, "Error loading audio to bytes", e);
    }

So the solution to skip the clicking noise is to use MODE_STATIC and setPlaybackHeadPosition function to skip the beginning of the audio file (that is probably the header or I don't know what). I hope that this part of code will help someone, I spent too many time trying to find a static mode code sample without finding a way to load a raw ressource.

Edit: After testing this solution on various devices, it appears that they have the clicking noise anyway.




回答3:


For "setPlaybackHeadPosition" to work, you have to play and pause first. It doesn't work if your track is stopped or not started. Trust me. This is dumb. But it works:

track.play();
track.pause();
track.setPlaybackHeadPosition(100);   
// then continue with track.write, track.play, etc. 



回答4:


In my case. It was WAV-header. And...

byte[] buf44 = new byte[44];
int read = inputStream.read(buf44, 0, 44);

...solved it.




回答5:


I found that Scott Stensland's reasoning was fitting my issue (thanks!).

I eliminated the pop by running a dead simple linear fade-in filter over the beginning of the sample array. The filter makes sample values start from 0 and slowly increase in amplitude to their original value. By always starting at a value of 0 at the zero cross over point the pop never occurs.

A similar fade-out filter was applied at the end of the sample array. The filter duration can easily be adjusted.

import android.util.Log;

public class FadeInFadeOutFilter
{
    private static final String TAG = FadeInFadeOutFilter.class.getSimpleName();

    private final int filterDurationInSamples;

    public FadeInFadeOutFilter ( int filterDurationInSamples )
    {
        this.filterDurationInSamples = filterDurationInSamples;
    }

    public void filter ( short[] audioShortArray )
    {
        filter(audioShortArray, audioShortArray.length);
    }

    public void filter ( short[] audioShortArray, int audioShortArraySize )
    {
        if ( audioShortArraySize/2 <= filterDurationInSamples ) {
            Log.w(TAG, "filtering audioShortArray with less samples filterDurationInSamples; untested, pops or even crashes may occur.  audioShortArraySize="+audioShortArraySize+", filterDurationInSamples="+filterDurationInSamples);
        }
        final int I = Math.min(filterDurationInSamples, audioShortArraySize/2);

        // Perform fade-in and fade-out simultaneously in one loop.
        final int fadeOutOffset = audioShortArraySize - filterDurationInSamples;
        for ( int i = 0 ; i < I ; i++ ) {
            // Fade-in beginning.
            final double fadeInAmplification = (double)i/I; // Linear ramp-up 0..1.
            audioShortArray[i] = (short)(fadeInAmplification * audioShortArray[i]);

            // Fade-out end.
            final double fadeOutAmplification = 1 - fadeInAmplification; // Linear ramp-down 1..0.
            final int j = i + fadeOutOffset;
            audioShortArray[j] = (short)(fadeOutAmplification * audioShortArray[j]);
        }
    }
}


来源:https://stackoverflow.com/questions/24790377/android-audiotrack-makes-a-click-sound-at-beginning

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!