java يعمل فئة Android AudioRecord-قم بمعالجة صوت الميكروفون المباشر بسرعة ، وقم بإعداد وظيفة رد الاتصال




كيفية تشغيل مايك سماعة الموبايل على الكمبيوتر (4)

أتساءل عما إذا كان بإمكانك الجمع بين هذه الإجابات بالطريقة التالية ...

استخدم setPositionNotificationPeriod (160) قبل حلقة while. هذا يجب أن يسبب استدعاء معاودة في كل مرة يتم قراءة إطارات 160. بدلاً من استدعاء عملية (المخزن المؤقت) داخل مؤشر الترابط الذي يقوم بإجراء حلقة القراءة عملية الاستدعاء (المخزن المؤقت) من رد الاتصال. استخدم متغير لتتبع المخزن المؤقت للقراءة الأخير حتى تقوم بمعالجة الصحيح. كما هو الآن ، يمكنك حظر القراءة ، فأنت لا تقرأ أثناء المعالجة. أعتقد أنه قد يكون من الأفضل فصل هذين الاثنين.

أريد تسجيل الصوت من الميكروفون والوصول إليه للتشغيل المحتمل في الوقت الفعلي تقريبًا. أنا غير متأكد من كيفية استخدام فئة Android AudioRecord لتسجيل بعض صوت الميكروفون والوصول إليه بسرعة.

بالنسبة لفئة AudioRecord ، يقول الموقع الرسمي "يستطلع التطبيق كائن AudioRecord في الوقت المناسب" ، ويحدد حجم المخزن المؤقت الذي يتم ملؤه طول الوقت للتسجيل قبل تشغيل البيانات غير المقروءة. في وقت لاحق ، اقترح أنه يجب استخدام مخزن مؤقت أكبر عند إجراء الاستقصاء بمعدل أقل. انهم في الواقع لا تظهر مثالا في التعليمات البرمجية.

أحد الأمثلة التي رأيتها في أحد الكتب يستخدم فئة AudioRecord لقراءة مخزن مؤقت يتم نشره حديثًا بصوت ميكروفون مباشر ، ثم يكتب التطبيق هذه البيانات إلى ملف SD. الشفرة الزائفة تبدو كشيء -

set up AudioRecord object with buffer size and recording format info
set up a file and an output stream
myAudioRecord.startRecording();
while(isRecording)
{
    // myBuffer is being filled with fresh audio
    read audio data into myBuffer
    send contents of myBuffer to SD file
}
myAudioRecord.stop();

كيف تتزامن هذه التعليمة البرمجية في قراءتها مع معدل التسجيل غير واضح - هو منطقي "isRecording" متسلسل وإيقاف بشكل صحيح في مكان آخر؟ يبدو أن هذه الشفرة يمكن أن تقرأ بشكل متكرر أو غير متكرر ، وهذا يتوقف على طول مدة القراءة والكتابة.

كما يشير مستند الموقع إلى أن فئة AudioRecord لها فئة متداخلة تسمى OnRecordPositionUpdateListener والتي يتم تعريفها كواجهة. تشير المعلومات إلى أنه بطريقة ما ، تقوم بتحديد الفترة التي تريد أن يتم إعلامك فيها بالتقدم في التسجيل ، واسم معالج الحدث الخاص بك ، ويتم إجراء مكالمة تلقائيًا إلى معالج الحدث الخاص بك بالتردد المحدد. أعتقد أن البنية ، في الشفرة الزائفة ستكون شيئًا مثل -

set target of period update message = myListener
set period to be about every 250 ms
other code

myListener()
{
    if(record button was recently tapped)
        handle message that another 250 ms of fresh audio is available
        ie, read it and send it somewhere
)

أحتاج إلى العثور على بعض الرموز المحددة التي تسمح لي بالتقاط ومعالجة صوت الميكروفون بتأخير يقل عن 500 مللي ثانية. يقدم Android فئة أخرى تسمى MediaRecorder ، ولكنه لا يدعم البث ، وقد أريد بث صوت ميكروفون مباشر عبر شبكة Wi-Fi في الوقت الفعلي تقريبًا. أين يمكنني العثور على بعض الأمثلة المحددة؟


فيما يلي التعليمة البرمجية التي تحتاج إليها لاستخدام OnRecordPositionUpdateListener وفترة الإعلام.

لقد لاحظت أنه من الناحية العملية لا يرسل الإخطار باستمرار في نفس الوقت بالضبط ، ولكني أريده ولكنه قريب بما فيه الكفاية.

حول detectAfterEvery :

يجب أن يكون حجم detectEvery كبيرًا بما يكفي لاستيعاب كمية البيانات التي تريدها فقط. لذلك في هذا المثال ، لدينا معدل عينة 44100 هرتز ، وهذا يعني أننا نريد 44100 عينة في الثانية. من خلال تعيين setPositionNotificationPeriod على 44100 ، يخبر الكود Android إلى معاودة الاتصال بعد تسجيل 44100 عينة ، أي حوالي ثانية واحدة.

الرمز الكامل موجود here :

        final int sampleRate = 44100;
        int bufferSize =
                AudioRecord.getMinBufferSize(sampleRate,
                        AudioFormat.CHANNEL_CONFIGURATION_MONO,
                        AudioFormat.ENCODING_PCM_16BIT);

//aim for 1 second
        int detectAfterEvery = (int)((float)sampleRate * 1.0f);

        if (detectAfterEvery > bufferSize)
        {
            Log.w(TAG, "Increasing buffer to hold enough samples " + detectAfterEvery + " was: " + bufferSize);
            bufferSize = detectAfterEvery;
        }

        recorder =
                new AudioRecord(AudioSource.MIC, sampleRate,
                        AudioFormat.CHANNEL_CONFIGURATION_MONO,
                        AudioFormat.ENCODING_PCM_16BIT, bufferSize);
        recorder.setPositionNotificationPeriod(detectAfterEvery);

        final short[] audioData = new short[bufferSize];
        final int finalBufferSize = bufferSize;

        OnRecordPositionUpdateListener positionUpdater = new OnRecordPositionUpdateListener()
        {
            @Override
            public void onPeriodicNotification(AudioRecord recorder)
            {
                Date d = new Date();
//it should be every 1 second, but it is actually, "about every 1 second"
//like 1073, 919, 1001, 1185, 1204 milliseconds of time.
                Log.d(TAG, "periodic notification " + d.toLocaleString() + " mili " + d.getTime());
                recorder.read(audioData, 0, finalBufferSize);

                //do something amazing with audio data
            }

            @Override
            public void onMarkerReached(AudioRecord recorder)
            {
                Log.d(TAG, "marker reached");
            }
        };
        recorder.setRecordPositionUpdateListener(positionUpdater);

        Log.d(TAG, "start recording, bufferSize: " + bufferSize);
        recorder.startRecording(); 

//remember to still have a read loop otherwise the listener won't trigger
while (continueRecording)
        {
            recorder.read(audioData, 0, bufferSize);
        }

بعد تجربة الكثير من الإخطارات ومجموعة من التقنيات الأخرى ، استقرت على هذا الرمز:

private class AudioIn extends Thread { 
     private boolean stopped    = false;

     private AudioIn() { 

             start();
          }

     @Override
     public void run() { 
            android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
            AudioRecord recorder = null;
            short[][]   buffers  = new short[256][160];
            int         ix       = 0;

            try { // ... initialise

                  int N = AudioRecord.getMinBufferSize(8000,AudioFormat.CHANNEL_IN_MONO,AudioFormat.ENCODING_PCM_16BIT);

                   recorder = new AudioRecord(AudioSource.MIC,
                                              8000,
                                              AudioFormat.CHANNEL_IN_MONO,
                                              AudioFormat.ENCODING_PCM_16BIT,
                                              N*10);

                   recorder.startRecording();

                   // ... loop

                   while(!stopped) { 
                      short[] buffer = buffers[ix++ % buffers.length];

                      N = recorder.read(buffer,0,buffer.length);
                      //process is what you will do with the data...not defined here
                      process(buffer);
                  }
             } catch(Throwable x) { 
               Log.w(TAG,"Error reading voice audio",x);
             } finally { 
               close();
             }
         }

      private void close() { 
          stopped = true;
        }

    }

حتى الآن تعمل بشكل قوي جدًا على نصف هواتف أندرويد التي جربتها.


private int freq =8000;
private AudioRecord audioRecord = null;
private Thread Rthread = null;

private AudioManager audioManager=null;
private AudioTrack audioTrack=null;
byte[] buffer = new byte[freq];

//call this method at start button

protected void Start()

{

loopback();

}

protected void loopback() { 

    android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
    final int bufferSize = AudioRecord.getMinBufferSize(freq,
            AudioFormat.CHANNEL_CONFIGURATION_MONO,
            AudioFormat.ENCODING_PCM_16BIT);


    audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, freq,
            AudioFormat.CHANNEL_CONFIGURATION_MONO,
            MediaRecorder.AudioEncoder.AMR_NB, bufferSize);

    audioTrack = new AudioTrack(AudioManager.ROUTE_HEADSET, freq,
            AudioFormat.CHANNEL_CONFIGURATION_MONO,
            MediaRecorder.AudioEncoder.AMR_NB, bufferSize,
            AudioTrack.MODE_STREAM);



    audioTrack.setPlaybackRate(freq);
     final byte[] buffer = new byte[bufferSize];
    audioRecord.startRecording();
    Log.i(LOG_TAG, "Audio Recording started");
    audioTrack.play();
    Log.i(LOG_TAG, "Audio Playing started");
    Rthread = new Thread(new Runnable() {
        public void run() {
            while (true) {
                try {
                    audioRecord.read(buffer, 0, bufferSize);                                    
                    audioTrack.write(buffer, 0, buffer.length);

                } catch (Throwable t) {
                    Log.e("Error", "Read write failed");
                    t.printStackTrace();
                }
            }
        }
    });
    Rthread.start();

}

يشغل الصوت المسجل أقل من 100 مللي تأخير.





android-hardware