Intereting Posts

Android: проблема класса AudioRecord: Callback никогда не вызывается

Моему Android Java Application необходимо записывать аудиоданные в ОЗУ и обрабатывать их. Вот почему я использую класс «AudioRecord», а не «MediaRecorder» (записывается только в файл).

До сих пор я использовал опрос занятого цикла с «read ()» для аудиоданных. Это работает до сих пор, но это слишком сильно перегружает процессор. Между двумя опросами я поставил поток спать, чтобы избежать использования 100% процессора. Однако это не очень чистое решение, так как время сна не гарантируется, и вы должны вычитать время безопасности, чтобы не потерять аудио фрагменты. Это не оптимальный процессор. Мне нужно столько бесплатных циклов процессора, сколько возможно для параллельного потока.

Теперь я выполнил запись, используя «OnRecordPositionUpdateListener». Это выглядит очень многообещающим и правильным способом сделать это согласно SDK Docs. Кажется, что все работает (открытие аудиоустройства, чтение () данных и т. Д.), Но Listner никогда не называется.

Кто-нибудь знает, почему?

Информация: Я работаю с настоящим устройством, а не под эмулятором. Запись с использованием Busy Loop в основном работает (однако не выполняется). Только вызывающий вызов никогда не вызывается.

Вот фрагмент моего исходного кода:

public class myApplication extends Activity { /* audio recording */ private static final int AUDIO_SAMPLE_FREQ = 16000; private static final int AUDIO_BUFFER_BYTESIZE = AUDIO_SAMPLE_FREQ * 2 * 3; // = 3000ms private static final int AUDIO_BUFFER_SAMPLEREAD_SIZE = AUDIO_SAMPLE_FREQ / 10 * 2; // = 200ms private short[] mAudioBuffer = null; // audio buffer private int mSamplesRead; // how many samples are recently read private AudioRecord mAudioRecorder; // Audio Recorder ... private OnRecordPositionUpdateListener mRecordListener = new OnRecordPositionUpdateListener() { public void onPeriodicNotification(AudioRecord recorder) { mSamplesRead = recorder.read(mAudioBuffer, 0, AUDIO_BUFFER_SAMPLEREAD_SIZE); if (mSamplesRead > 0) { // do something here... } } public void onMarkerReached(AudioRecord recorder) { Error("What? Hu!? Where am I?"); } }; ... public void onCreate(Bundle savedInstanceState) { try { mAudioRecorder = new AudioRecord( android.media.MediaRecorder.AudioSource.MIC, AUDIO_SAMPLE_FREQ, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, AUDIO_BUFFER_BYTESIZE); } catch (Exception e) { Error("Unable to init audio recording!"); } mAudioBuffer = new short[AUDIO_BUFFER_SAMPLEREAD_SIZE]; mAudioRecorder.setPositionNotificationPeriod(AUDIO_BUFFER_SAMPLEREAD_SIZE); mAudioRecorder.setRecordPositionUpdateListener(mRecordListener); mAudioRecorder.startRecording(); /* test if I can read anything at all... (and yes, this here works!) */ mSamplesRead = mAudioRecorder.read(mAudioBuffer, 0, AUDIO_BUFFER_SAMPLEREAD_SIZE); } } 

Solutions Collecting From Web of "Android: проблема класса AudioRecord: Callback никогда не вызывается"

Я считаю, что проблема в том, что вам все равно нужно сделать цикл чтения. Если вы настроите обратные вызовы, они будут срабатывать, когда вы прочтете количество кадров, которые вы указали для обратных вызовов. Но вам все равно нужно читать. Я пробовал это, и вызовы вызываются просто отлично. Настройка маркера вызывает обратный вызов, когда это число кадров было считано с начала записи. Другими словами, вы можете установить маркер далеко в будущее, после многих из ваших чтений, и он будет срабатывать тогда. Вы можете установить период на некоторое большее количество кадров, и этот обратный вызов будет срабатывать каждый раз, когда будет считываться количество кадров. Я думаю, что они делают это, поэтому вы можете выполнять низкоуровневую обработку необработанных данных в узком цикле, тогда каждый раз ваш обратный вызов может выполнять обработку на итоговом уровне. Вы можете использовать маркер, чтобы было легче решить, когда прекратить запись (вместо подсчета в цикле чтения).

Вот мой код, используемый для поиска среднего шума. Обратите внимание, что оно основано на уведомлениях слушателя, чтобы сохранить аккумулятор устройства. Это определенно основано на примерах выше. Этот пример спас мне много времени, спасибо.

 private AudioRecord recorder; private boolean recorderStarted; private Thread recordingThread; private int bufferSize = 800; private short[][] buffers = new short[256][bufferSize]; private int[] averages = new int[256]; private int lastBuffer = 0; protected void startListenToMicrophone() { if (!recorderStarted) { recordingThread = new Thread() { @Override public void run() { int minBufferSize = AudioRecord.getMinBufferSize(8000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT); recorder = new AudioRecord(AudioSource.MIC, 8000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 10); recorder.setPositionNotificationPeriod(bufferSize); recorder.setRecordPositionUpdateListener(new OnRecordPositionUpdateListener() { @Override public void onPeriodicNotification(AudioRecord recorder) { short[] buffer = buffers[++lastBuffer % buffers.length]; recorder.read(buffer, 0, bufferSize); long sum = 0; for (int i = 0; i < bufferSize; ++i) { sum += Math.abs(buffer[i]); } averages[lastBuffer % buffers.length] = (int) (sum / bufferSize); lastBuffer = lastBuffer % buffers.length; } @Override public void onMarkerReached(AudioRecord recorder) { } }); recorder.startRecording(); short[] buffer = buffers[lastBuffer % buffers.length]; recorder.read(buffer, 0, bufferSize); while (true) { if (isInterrupted()) { recorder.stop(); recorder.release(); break; } } } }; recordingThread.start(); recorderStarted = true; } } private void stopListenToMicrophone() { if (recorderStarted) { if (recordingThread != null && recordingThread.isAlive() && !recordingThread.isInterrupted()) { recordingThread.interrupt(); } recorderStarted = false; } } 

Теперь я выполнил запись, используя «OnRecordPositionUpdateListener». Это выглядит очень многообещающим и правильным способом сделать это согласно SDK Docs. Кажется, что все работает (открытие аудиоустройства, чтение () данных и т. Д.), Но Listner никогда не называется.

Кто-нибудь знает, почему?

Я обнаружил, что OnRecordPositionUpdateListener игнорируется, пока вы не сделаете свой первый .read() .

Другими словами, я обнаружил, что, если я настрою все на документы, мой Слушатель никогда не вызывался. Однако, если я сначала вызвал .read() сразу после выполнения моего начального .start() то Listener будет вызван – при условии, что я сделал .read() каждый раз, когда был вызван Listener.

Другими словами, это почти похоже на то, что событие Listener подходит только один раз на .read() или некоторые из них.

Я также обнаружил, что если бы я попросил читать меньше, чем buffSize / 2, чтобы прослушиватель не вызывался. Таким образом, кажется, что слушатель называется только ПОСЛЕ .read() по крайней мере, половины размера буфера. Чтобы продолжать использовать обратный вызов Listener, нужно вызывать чтение каждый раз, когда выполняется прослушиватель. (Другими словами, поместите вызов для чтения в коде Listener.)

Тем не менее, слушатель, кажется, вызывается в то время, когда данные еще не готовы, что вызывает блокировку.

Кроме того, если ваш период уведомления или время превышают половину вашего буфера, они никогда не будут вызваны, похоже.

ОБНОВИТЬ:

Поскольку я продолжаю копать глубже, я обнаружил, что обратный вызов, кажется, ТОЛЬКО будет вызван, когда .read() заканчивается ……!

Я не знаю, если это ошибка или функция. Моя первоначальная мысль заключалась в том, что я хочу обратный вызов, когда пришло время читать. Но, возможно, разработчики андроидов придерживались идеи по-другому, где вы только что положили while(1){xxxx.read(...)} в поток самостоятельно и чтобы не было необходимости отслеживать Каждый раз, когда read () закончен, обратный вызов может по существу сказать вам, когда чтение закончилось.

Ой. Может быть, это просто осенило меня. И я думаю, что другие указали это выше меня здесь, но оно не утонуло: обратный вызов позиции или периода должен работать с байтами, которые были прочитаны уже …

Возможно, я застрял в использовании потоков.

В этом стиле, как только он вернется, поток будет непрерывно вызывать read (), поскольку чтение блокирует и терпеливо ждет достаточного количества данных для возврата.

Затем, независимо, обратный вызов будет вызывать вашу указанную функцию каждые x число выборок.

Для тех, кто записывает аудио через Intent Service, они могут также столкнуться с этой проблемой обратного вызова. В последнее время я занимаюсь этим вопросом, и я придумал простое решение, в котором вы называете свой метод записи в отдельном потоке. Это должно вызвать методPeriodicNotification во время записи без каких-либо проблем.

Что-то вроде этого:

 public class TalkService extends IntentService { ... @Override protected void onHandleIntent(Intent intent) { Context context = getApplicationContext(); tRecord = new Thread(new recordAudio()); tRecord.start(); ... while (tRecord.isAlive()) { if (getIsDone()) { if (aRecorder.getRecordingState() == AudioRecord.RECORDSTATE_STOPPED) { socketConnection(context, host, port); } } } } ... class recordAudio implements Runnable { public void run() { try { OutputStream osFile = new FileOutputStream(file); BufferedOutputStream bosFile = new BufferedOutputStream(osFile); DataOutputStream dosFile = new DataOutputStream(bosFile); aRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelInMode, encodingMode, bufferSize); data = new short[bufferSize]; aRecorder.setPositionNotificationPeriod(sampleRate); aRecorder .setRecordPositionUpdateListener(new AudioRecord.OnRecordPositionUpdateListener() { int count = 1; @Override public void onPeriodicNotification( AudioRecord recorder) { Log.e(WiFiDirect.TAG, "Period notf: " + count++); if (getRecording() == false) { aRecorder.stop(); aRecorder.release(); setIsDone(true); Log.d(WiFiDirect.TAG, "Recorder stopped and released prematurely"); } } @Override public void onMarkerReached(AudioRecord recorder) { // TODO Auto-generated method stub } }); aRecorder.startRecording(); Log.d(WiFiDirect.TAG, "start Recording"); aRecorder.read(data, 0, bufferSize); for (int i = 0; i < data.length; i++) { dosFile.writeShort(data[i]); } if (aRecorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) { aRecorder.stop(); aRecorder.release(); setIsDone(true); Log.d(WiFiDirect.TAG, "Recorder stopped and released"); } } catch (Exception e) { // TODO: handle exception } } }