Android声音播放延迟到while循环结束

Posted

技术标签:

【中文标题】Android声音播放延迟到while循环结束【英文标题】:Android sound playback delayed until while loop ends 【发布时间】:2015-03-09 00:22:42 【问题描述】:

我遇到了这个让我很难受的安卓问题。

我正在开发一个应用程序,只要手机通过麦克风感应到噪音,我就会播放声音。逻辑的核心是一个 while 循环,它调用另一个类的声音播放方法。

当设置方法将循环的条件切换为false 时,该循环终止。问题出现在播放延迟到循环条件设置为false 时。循环结束后,它会以混乱的方式一次播放对声音播放方法的调用,而不是在每次循环中调用它时。

尽管线程化它并将声音播放方法调用粘贴在runOnUiThread 中,但仍然会发生这种情况。 MediaPlayer 和SoundPool 也会发生这种情况。我只希望playFart() 方法在被调用时准确播放。

NoiseActivatedFart

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageButton;
import android.widget.TextView;

public class NoiseActivatedFart extends Activity 

    public static int raw = 0; //For debugging. Will be removed
    public static int trigger = 0; //For debugging. Will be removed
    private Animation press;
    private boolean activate_button_state = true;
    private EvaluateAmbientNoise evaluateAmbientNoise = new EvaluateAmbientNoise(this);

    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.noise_activated_fart);

        final TextView debugTextView = (TextView) findViewById(R.id.debug);
        press = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.press);
        final ImageButton activateButton = (ImageButton) findViewById(R.id.activateButton);

        activateButton.setOnClickListener(new View.OnClickListener() 
            public void onClick(View v) 
                if (activate_button_state) 
                    activateButton.startAnimation(press);
                    activate_button_state = false;

                    //The Android.MediaRecorder documentation recommends that objects
                    //be used in their own threads, as it can really drag the main thread down

                    new Thread() 
                        public void run()
                            evaluateAmbientNoise.startListening();
                        
                    .start();
                
                else 
                    activateButton.startAnimation(press);
                    debugTextView.setText(raw + " " + trigger);
                    activate_button_state = true;
                    evaluateAmbientNoise.stopListening();
                    System.out.println("stop listening");
                
            
        );
    

评估环境噪声

import android.content.Context;
import android.media.MediaRecorder;
import android.app.Activity;

class EvaluateAmbientNoise implements Runnable 

    Activity activity = new Activity();
    int[] average = new int[10];
    int averageNoiseLevel = 1;
    int noiseTriggerLevel = 1;
    Context context;
    FartPlayer fartplayer = new FartPlayer();
    MediaRecorder recorder = null;
    boolean recorder_state = false;
    boolean keepGoing = false;

    /**
     * Constructor
     * The SoundPool object that is utilized by "fartPlayer"
     * requires a context object to function, and gets it from here
     */
    EvaluateAmbientNoise(Context input_context) 
        context = input_context;
    

    public void run() 
        startListening();
    

    /**
     * startListening prepares and starts an Android.MediaRecorder object that is
     * used for it's getMaxAmplitude() function. It checks the level of
     * noise several times, averages them, and uses the average to determine a trigger level.
     * It continues to check the levels and play a fart whenever the levels exceed the trigger.
     * It continues until stopListening is called.
     */

    public void startListening() 
        prepareMediaRecorder();
        startMediaRecorder();
        fartplayer.initSounds(context);

        //Gather sound levels, average them, and use it to make a trigger
        averageNoiseLevel = 0;

        for (int counter = 0; counter < 10; counter++) 
            average[counter] = recorder.getMaxAmplitude();
            System.out.println("getting values: " + average[counter]);
        
        for (int i:average)
            averageNoiseLevel += i;
            System.out.println("averaging array: " + averageNoiseLevel);
        
        averageNoiseLevel = averageNoiseLevel / average.length;
        noiseTriggerLevel = (int)(averageNoiseLevel * 1.5);

        System.out.println("average total : " + averageNoiseLevel);
        System.out.println("trigger: " + noiseTriggerLevel);

        //Listen for noise, and play a fart when the noise exceeds the trigger
        keepGoing = true;

        int currentLevel = 0;
        while (keepGoing) 
            currentLevel = recorder.getMaxAmplitude();
            if (currentLevel > (noiseTriggerLevel)) 
                activity.runOnUiThread(new Runnable() 
                    public void run() 
                        fartplayer.playFart();
                    
                );
            
            try 
                Thread.sleep(3000);
             catch (InterruptedException e) 
                System.err.println("Caught Exception: " + e.getMessage());
            

            System.out.println("tick");
        
    

    public static void sleep() 
        System.out.println("sleeping 1000ms");
    

    // Stops "startListening()"
    public void stopListening() 
        keepGoing = false;
        stopMediaRecorder();
    

    /**
     * start and stopMediaRecorder essentially staples a crude state flag to an
     * Android.MediaRecorder object. This is needed because of the object's different states,
     * and its simultaneous lack of state-checking functions.
     */
    private void startMediaRecorder() 
        if (recorder_state == false) 
            recorder.start();
            recorder_state = true;
        
    

    private void stopMediaRecorder() 
        if (recorder_state == true) 
            recorder.stop();
            recorder.release();
            recorder = null;
            recorder_state = false;
        
    

    /**
     * prepareMediaRecorder initializes the Android.MediaRecorder object before use.
     * Any calls to MediaRecorder throw an exception otherwise
     */
    public void prepareMediaRecorder() 
        recorder = new MediaRecorder();
        recorder.setAudiosource(MediaRecorder.AudioSource.MIC);
        recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
        recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
        recorder.setOutputFile("/dev/null/");

        try 
            recorder.prepare();
        
        catch (Exception e)
            System.out.println("Caught IOException: " + e.getMessage());
        
    

放屁者

import android.app.Activity;
import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class FartPlayer extends Activity 

    private final Random fartSelector = new Random();
    private List<Integer> fartList = new ArrayList<Integer>();
    SoundPool sp;

    //Initializes the SoundPool object and loads some fart mp3s into it
    public void initSounds(Context c) 
        sp = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
        fartList.add(sp.load(c, R.raw.fart1, 1));
        fartList.add(sp.load(c, R.raw.fart2, 1));
        fartList.add(sp.load(c, R.raw.fart3, 1));
        fartList.add(sp.load(c, R.raw.fart4, 1));
        fartList.add(sp.load(c, R.raw.fart5, 1));
        fartList.add(sp.load(c, R.raw.fart6, 1));
    

    //Plays a random fart from the SoundPool when called
    public void playFart() 
        sp.play(fartList.get(fartSelector.nextInt(fartList.size())),1f,1f,1,0,1f);
        System.out.println("farting");
    

【问题讨论】:

【参考方案1】:

问题出在这里:

public void initSounds(Context c) 
    sp = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
    fartList.add(sp.load(c, R.raw.fart1, 1));
    fartList.add(sp.load(c, R.raw.fart2, 1));
    fartList.add(sp.load(c, R.raw.fart3, 1));
    fartList.add(sp.load(c, R.raw.fart4, 1));
    fartList.add(sp.load(c, R.raw.fart5, 1));
    fartList.add(sp.load(c, R.raw.fart6, 1));

它一次加载所有声音,一旦播放,每个声音都会一次播放,因为每个声音都是一次加载和播放的。您可以尝试使用 MediaPlayer:

import android.content.Context;
import android.media.MediaPlayer;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class FartPlayer 

    private Random fartSelector = new Random();
    private List<Integer> songs;
    Context c;
    MediaPlayer mp;
    //Initializes the SoundPool object and loads some fart mp3s into it
    public void initSounds(Context c) 
        this.c = c;
        mp = new MediaPlayer();
        songs = new ArrayList<Integer>();
        songs.add(0, fart1);//starts at 0 so random isnt messed up
        songs.add(1, fart2);
        songs.add(2, fart3);
        songs.add(3, fart4);
        songs.add(4, fart5);
        songs.add(5, fart6);
    

    //Plays a random fart from the SoundPool when called
    public void playFart()
        int song;
        song = songs.get(fartSelector.nextInt(songs.size()));//selects a random number from 0-5(up to 6)
        mp = MediaPlayer.create(c, song);//sets up the output
        mp.start();//plays
        System.out.println("farting");
    



但是,可以通过加载一个声音来解决:

Random r = new Random();
public void initSounds(Context c) 
    this.c = c;
    sp = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
    songs = new ArrayList<Integer>();
    songs.add(0, fart1);//starts at 0 so random isnt messed up
    songs.add(1, fart2);
    songs.add(2, fart3);
    songs.add(3, fart4);
    songs.add(4, fart5);
    songs.add(5, fart6);


//Plays a random fart from the SoundPool when called
public void playFart()
    int song;
    song = songs.get(fartSelector.nextInt(songs.size()));//selects a random number from 0-5(up to 6)
    sp.load(c, song, 1);//if this doesnt work, comment this answer
    sp.play(fartList.get(r.nextInt(fartList.size())));
    System.out.println("farting");

【讨论】:

以上是关于Android声音播放延迟到while循环结束的主要内容,如果未能解决你的问题,请参考以下文章

如何延迟 For 循环直到声音在 Swift 中播放完毕

如何在Android中无间隙地循环播放声音?

JQuery 不播放多个声音文件或延迟事件

我的代码就像在一个while循环中一样,即使它不是

适用于 Android 的 Adob​​e 原生扩展以播放声音

连续播放声音 onTouch()