如何在安卓系统中持续后台录制音频/声音?

15

我想在后台录制音频,所以我使用了service。但是我无法在service中录制音频。

我在Activity中尝试了相同的代码,它对我有用。但当有语音输入时如何在后台进行音频录制,也就是说,如果有语音输入,音频录制应该开始,并且应该在后台进行...?


所有这些奇怪的缩进和 > 符号是什么意思? - Cylindric
2个回答

24

在我的一个项目中,我有一个连续录制麦克风音频的需求。我不能分享整个项目,但我可以分享特定的AudioRecorder类。

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;

    public class AudioRecorder {
        public enum State {
            INITIALIZING,
            READY,
            RECORDING,
            ERROR,
            STOPPED
        };
        private byte[] audioBuffer = null;
        private int source = MediaRecorder.AudioSource.MIC;
        private int sampleRate = 0;
        private int encoder = 0;
        private int nChannels = 0;
        private int bufferRead = 0;
        private int bufferSize = 0;
        private RandomAccessFile tempAudioFile = null;
        public AudioRecord audioRecorder = null;
        private State state;
        private short bSamples = 16;
        private int framePeriod;

        // The interval in which the recorded samples are output to the file
        // Used only in uncompressed mode
        private static final int TIMER_INTERVAL = 120;
        volatile Thread t = null;
        public int TimeStamp = 0, count = 0, preTimeStamp = 0;

        public AudioRecorder(Context c) {
            this.sampleRate = 11025;
            this.encoder = AudioFormat.ENCODING_PCM_16BIT;
            this.nChannels = AudioFormat.CHANNEL_CONFIGURATION_MONO;
            this.preTimeStamp = (int) System.currentTimeMillis();
            myApp = (MyApp) c.getApplicationContext();
            mQueue = myApp.getQueue();

            try {
                /*          
                    String fileName = "/sdcard/XYZ/11025.wav";
                    tempAudioFile = new RandomAccessFile(fileName,"rw");
                */

                framePeriod = sampleRate * TIMER_INTERVAL / 1000;
                bufferSize = framePeriod * 2 * bSamples * nChannels / 8;

                if (bufferSize < AudioRecord.getMinBufferSize(sampleRate, nChannels, encoder)) {
                    bufferSize = AudioRecord.getMinBufferSize(sampleRate, nChannels, encoder);

                    // Set frame period and timer interval accordingly
                    framePeriod = bufferSize / (2 * bSamples * nChannels / 8);
                    Log.w(AudioRecorder.class.getName(), "Increasing buffer size to " + Integer.toString(bufferSize));
                }

                audioRecorder = new AudioRecord(source, sampleRate, nChannels, encoder, bufferSize);
                audioBuffer = new byte[2048];
                audioRecorder.setRecordPositionUpdateListener(updateListener);
                audioRecorder.setPositionNotificationPeriod(framePeriod);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

        private AudioRecord.OnRecordPositionUpdateListener updateListener = new AudioRecord.OnRecordPositionUpdateListener() {
            @Override
            public void onPeriodicNotification(AudioRecord recorder) {
                //          Log.d(Constant.APP_LOG,"Into Periodic Notification...");
            }
            catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            */
        }

        @Override
        public void onMarkerReached(AudioRecord recorder) {
            // TODO Auto-generated method stub
        }
    };

    public void start() {
        if (state == State.INITIALIZING) {
            audioRecorder.startRecording();
            state = State.RECORDING;

            t = new Thread() {
                public void run() {
                    //Here You can read your Audio Buffers
                    audioRecorder.read(audioBuffer, 0, 2048);
                }
            };

            t.setPriority(Thread.MAX_PRIORITY);
            t.start();
        } else {
            Log.e(AudioRecorder.class.getName(), "start() called on illegal state");
            state = State.ERROR;
        }
    }

    public void stop() {
        if (state == State.RECORDING) {
            audioRecorder.stop();
            Thread t1 = t;
            t = null;
            t1.interrupt();
            count = 0;
            state = State.STOPPED;
        } else {
            Log.e(AudioRecorder.class.getName(), "stop() called on illegal state");
            state = State.ERROR;
        }
    }

    public void release() {
        if (state == State.RECORDING) {
            stop();
        }

        if (audioRecorder != null) {
            audioRecorder.release();
        }
    }

    public void reset() {
        try {
            if (state != State.ERROR) {
                release();
            }
        } catch (Exception e) {
            Log.e(AudioRecorder.class.getName(), e.getMessage());

            state = State.ERROR;
        }
    }

    public State getState() {
        return state;
    }
}

现在,创建服务并调用start()方法,操作记录的音频缓冲区以实现你的目的。
希望这能帮到你。

嗨@NISHANT,为什么你设置了“t.setPriority(Thread.MAX_PRIORITY);”?你能解释一下mQueue吗?它与项目有关吗? - NrNazifi
1
@prosoft:在我的项目中,有许多后台线程同时运行,为了避免优先级反转问题,我将最大优先级设置为录制线程。mQueue是一个FIFO队列变量,它保存从PCM转换为AAC的缓冲区并将它们发送到网络。 - Nishant Rajput
@N.Droid:感谢您分享如此完整的代码。这段代码附带有许可证吗? - njzk2
1
你的代码中是什么意思?myApp = (MyApp) c.getApplicationContext(); mQueue = myApp.getQueue(); - Qadir Hussain
@QadirHussain 这是Android应用程序对象和队列对象。 - Nishant Rajput
显示剩余5条评论

2

要在后台开始录制,您可以选择以下两种方法:

  • 创建一个线程并在其内部进行录制。

  • 创建一个服务,该服务将在后台运行。

希望这有所帮助。

编辑 1

Thread recordInBackGround= new Thread(new Runnable() {
                    @Override
                    public void run() { 
//Your recording portion of the code goes here.
}
});

recordInBackGround.start();

1
我已经在使用线程和服务,但它正在产生运行时异常。 - Dipali
如果您有任何在后台进行录制的项目链接,请分享。 - Dipali
请在帖子中附上您的代码和运行时异常信息,我们会尽力帮助您解决问题。 - Deva
请问您能否解释一下“创建一个在后台运行的服务”是什么意思? - Jithu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接