2

I need your help. I'm trying to record some audio with Androids AudioRecord class. Most time this works very well but sometimes I get a SIGSEGV error.

Here is the code which runs in a seperate Thread while recording audio:

private void startRecording() {
    recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
            RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING, bufferSize);

    recorder.startRecording();

    isRecording = true;

    recordingThread = new Thread(new Runnable() {

        @Override
        public void run() {
            writeAudioDataToFile();
        }
    }, "AudioRecorder Thread");

    recordingThread.start();
}

private void writeAudioDataToFile() {
    byte data[] = new byte[bufferSize];
    String filename = getTempFilename();
    FileOutputStream os = null;

    try {
        os = new FileOutputStream(filename);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }

    int read = 0;

    if (null != os) {
        while (isRecording) {
            read = recorder.read(data, 0, bufferSize);

            if (AudioRecord.ERROR_INVALID_OPERATION != read) {
                try {
                    os.write(data);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        try {
            os.close();
            os.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

And here is what I do, when the recording stops:

public void stopRecording() {
    if (null != recorder) {
        isRecording = false;
        mRecordingBlocked = true;
        mProgressDialog = RecorderDialog.newInstance();
        mProgressDialog.show(getFragmentManager(), "progressDialog");

        recorder.stop();
        recorder.release();

        recorder = null;
        recordingThread = null;

        new Thread(new Runnable() {
            @Override
            public void run() {
                String fileName = getFilename();
                copyWaveFile(getTempFilename(), fileName);

                final TelephonyManager tm = (TelephonyManager) getActivity().getBaseContext().getSystemService(Context.TELEPHONY_SERVICE);

                final String tmDevice, tmSerial, androidId;
                tmDevice = "" + tm.getDeviceId();
                tmSerial = "" + tm.getSimSerialNumber();
                androidId = "" + android.provider.Settings.Secure.getString(getActivity().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);

                UUID deviceUuid = new UUID(androidId.hashCode(), ((long) tmDevice.hashCode() << 32) | tmSerial.hashCode());
                String deviceId = deviceUuid.toString();
                App.db.insertEntry(deviceId, "Song title", fileName);

                deleteTempFile();

                if (cbox_overdub.isChecked()) {
                    phrases.add(playAudio(fileName, true));
                }
            }
        }).start();
    }

}

private void copyWaveFile(String inFilename, String outFilename) {
    FileInputStream in;
    FileOutputStream out;
    long totalAudioLen = 0;
    long totalDataLen;
    long longSampleRate = RECORDER_SAMPLERATE;
    int channels = 2;
    long byteRate = RECORDER_BPP * RECORDER_SAMPLERATE * channels / 8;

    try {
        AppLog.logString("begin copyWaveFile try{}");
        File file = new File(inFilename);
        in = new FileInputStream(file);
        byte[] bytes = new byte[(int) file.length()];
        totalAudioLen = in.getChannel().size();
        totalDataLen = totalAudioLen + 36;
        in.read(bytes);
        in.close();

        out = new FileOutputStream(outFilename);
        AppLog.logString("fos created");

        WriteWaveFileHeader(out, totalAudioLen, totalDataLen,
                longSampleRate, channels, byteRate);
        AppLog.logString("wave header written");

        int bufferLength = 1024;
        publishProgress(0);
        for (int i = 0; i < bytes.length; i += bufferLength) {
            int progress = (int) ((i / (float) bytes.length) * 100);
            publishProgress(progress);
            if (bytes.length - i >= bufferLength) {
                out.write(bytes, i, bufferLength);
            } else {
                out.write(bytes, i, bytes.length - i);
            }
        }
        publishProgress(100);
        AppLog.logString("progress complete");
        mRecordingBlocked = false;
        mProgressDialog.dismiss();

        out.close();
        out.flush();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

It seems this error occurs after the instance of AudioRecord has already been stopped. Then I copy the contents of my temporary temp_record.wav (SD Card) to the destination (SD Card) with setting a proper wave file header.

I'm not able to filter any useful information out of the following debug information. I hope someone has an idea. Thank you!

Here's the Logcat output:

06-30 14:47:53.311: INFO/DEBUG(9381): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
06-30 14:47:53.311: INFO/DEBUG(9381): Build fingerprint: 'otorola/RTGB/umts_milestone2:2.3.4/MILS2_U6_4.1-22/1317097892:user/release-keys'
06-30 14:47:53.311: INFO/DEBUG(9381): pid: 9459, tid: 9525  >>> de.intermeco.android.apps.ilaugh <<<
06-30 14:47:53.311: INFO/DEBUG(9381): signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 4640e000
06-30 14:47:53.311: INFO/DEBUG(9381): r0 4640e000  r1 00000000  r2 fffffd49  r3 4670da18
06-30 14:47:53.311: INFO/DEBUG(9381): r4 4640e000  r5 4640e000  r6 fffffd49  r7 4670da18
06-30 14:47:53.311: INFO/DEBUG(9381): r8 a904cf6a  r9 0000000a  10 a904cfad  fp a904cff0
06-30 14:47:53.311: INFO/DEBUG(9381): ip a9060074  sp 4670d9e0  lr afd11704  pc afd113dc  cpsr 20000050
06-30 14:47:53.311: INFO/DEBUG(9381): d0  6472656767756265  d1  0037fff00020000c
06-30 14:47:53.311: INFO/DEBUG(9381): d2  fff5ffd8fff5ffdb  d3  ffe0ffd8ffd9ffd8
06-30 14:47:53.311: INFO/DEBUG(9381): d4  fff2ffe50008ffd4  d5  ffdafffafff20000
06-30 14:47:53.311: INFO/DEBUG(9381): d6  fff40003ffcf0003  d7  ffe30013ffe1fffd
06-30 14:47:53.311: INFO/DEBUG(9381): d8  0000000000989680  d9  0000000000000000
06-30 14:47:53.311: INFO/DEBUG(9381): d10 0000000000000000  d11 0000000000000000
06-30 14:47:53.311: INFO/DEBUG(9381): d12 0000000000000000  d13 0000000000000000
06-30 14:47:53.311: INFO/DEBUG(9381): d14 0000000000000000  d15 0000000000000000
06-30 14:47:53.311: INFO/DEBUG(9381): d16 000000c24003a7e0  d17 4000000000000000
06-30 14:47:53.311: INFO/DEBUG(9381): d18 3ff0000000000000  d19 0000000000000000
06-30 14:47:53.311: INFO/DEBUG(9381): d20 b96377ce858a5d48  d21 3929f5135cb87c55
06-30 14:47:53.311: INFO/DEBUG(9381): d22 3e21ee9ebdb4b1c4  d23 bda8fae9be8838d4
06-30 14:47:53.319: INFO/DEBUG(9381): d24 0000000000000000  d25 0000000000000000
06-30 14:47:53.319: INFO/DEBUG(9381): d26 0000000000000000  d27 ffffffffffffffff
06-30 14:47:53.319: INFO/DEBUG(9381): d28 0100010001000100  d29 0100010001000100
06-30 14:47:53.319: INFO/DEBUG(9381): d30 4086800000000000  d31 3ff0000000000000
06-30 14:47:53.319: INFO/DEBUG(9381): scr 60000010
06-30 14:47:53.483: INFO/DEBUG(9381): #00  pc 000113dc  /system/lib/libc.so (pthread_mutex_lock)
06-30 14:47:53.483: INFO/DEBUG(9381): #01  pc 00011700  /system/lib/libc.so (__pthread_cond_timedwait_relative)
06-30 14:47:53.483: INFO/DEBUG(9381): #02  pc 0002d658  /system/lib/libmedia.so
06-30 14:47:53.483: INFO/DEBUG(9381): code around pc:
06-30 14:47:53.483: INFO/DEBUG(9381): afd113bc e3a02001 ebfffe82 e1a00005 e8bd87f0
06-30 14:47:53.483: INFO/DEBUG(9381): afd113cc 00036024 e92d47f0 e2504000 0a000019
06-30 14:47:53.483: INFO/DEBUG(9381): afd113dc e5946000 e5947000 e2166903 1a000017
06-30 14:47:53.483: INFO/DEBUG(9381): afd113ec e5945000 e1a02004 e2055a02 e1a00005
06-30 14:47:53.483: INFO/DEBUG(9381): afd113fc e3851001 ebffed7f e3500000 13856002
06-30 14:47:53.483: INFO/DEBUG(9381): code around lr:
06-30 14:47:53.483: INFO/DEBUG(9381): afd116e4 e1a03007 e1a02006 e2011001 e1a00004
06-30 14:47:53.483: INFO/DEBUG(9381): afd116f4 ebfffd9a e1a04000 e1a00005 ebffff32
06-30 14:47:53.483: INFO/DEBUG(9381): afd11704 e374006e 03a0006e 13a00000 e8bd81f0
06-30 14:47:53.483: INFO/DEBUG(9381): afd11714 e304cdd3 e3043240 e92d4010 e341c062
06-30 14:47:53.483: INFO/DEBUG(9381): afd11724 e1a0e002 e24dd008 e340300f e1a0200d
06-30 14:47:53.483: INFO/DEBUG(9381): stack:
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9a0  04000804
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9a4  00000001
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9a8  001c6b58
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9ac  00000262
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9b0  0000ee6b
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9b4  afd0fe34  /system/lib/libc.so
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9b8  001edbb0
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9bc  afd13bc1  /system/lib/libc.so
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9c0  001b0470
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9c4  001c6b58
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9c8  00000241
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9cc  3b9aca00
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9d0  00000000
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9d4  00989680
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9d8  df002777
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9dc  e3a070ad
06-30 14:47:53.483: INFO/DEBUG(9381): #00 4670d9e0  00000000
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9e4  4640e000
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9e8  fffffd49
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9ec  4670da18
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9f0  a904cf6a  /system/lib/libmedia.so
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9f4  0000000a
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9f8  a904cfad  /system/lib/libmedia.so
06-30 14:47:53.483: INFO/DEBUG(9381): 4670d9fc  afd11704  /system/lib/libc.so
06-30 14:47:53.483: INFO/DEBUG(9381): #01 4670da00  4640e000
06-30 14:47:53.483: INFO/DEBUG(9381): 4670da04  4640e004
06-30 14:47:53.483: INFO/DEBUG(9381): 4670da08  4670da98
06-30 14:47:53.483: INFO/DEBUG(9381): 4670da0c  000000c8
06-30 14:47:53.483: INFO/DEBUG(9381): 4670da10  a904cf6a  /system/lib/libmedia.so
06-30 14:47:53.483: INFO/DEBUG(9381): 4670da14  a902d65b  /system/lib/libmedia.so
Chris
  • 295
  • 1
  • 4
  • 11
  • Hi Chris, I think it's impossible to help with this informations. The debug log does not show any hint. Are you able to reproduce the error? Can you provide any code where it fails? I have a feeling in my stomach that it's some sort of asynchronous problem. – Diego Frehner Jun 30 '12 at 14:22
  • The error occurs from time to time but is not reproducible with a special "todo list". Sometimes the error happens at the first record sometimes many records later. I think you may be right that it is an asynchronous problem, cause I'm using a seperate thread writing audio to file. I will try to look a bit deeper at that part of code. – Chris Jun 30 '12 at 14:47

5 Answers5

3

Thanks for pointing out SIGSEV again, i over read it a little because i didn't know its meaning. Now i do :) I agree with CommonsWare that you should inform the manufacturer/modder of the firmware. But i also think it does not help you further in a useful time span. Maybe we find a way to modify your code in a way not to provoke the error anymore.

Generally your code looks quite good. What i think is not totally safe and proper is the way you are stopping the recorder.

  1. Instead of using the isRecording flag i'd use a isCancelled flag. So you record while(!isCancelled). And when you want to stop recording you just set isCancelled to true and call recorder.stop().
  2. After recording stopped you can call, right after writeAudioDataToFile() in the audio recorder thread, your logic for display the progress dialog and for copy the file. I would not use an additional thread for these things since we are still in the audio recorder thread and your UI is not blocked.
  3. When to call recorder.release(), it releases memory... The docs "Releases the native AudioRecord resources." I don' know exactly, if this has an influence on the problem. In the current code you provided, you call recorder.release() while theoretically the recorder could reading (without knowing if the class is threadsafe). Maybe you could also try to call it after you copied all your files, even i think it should be fine after the recorder really stopped and your outputstream is closed.

Hope i could help a little and you are able to find a way.

Diego Frehner
  • 2,396
  • 1
  • 28
  • 35
2

Thank you for your proposals. I rebuild the code a little bit and now it seems to work. At least I did not get the error for about one day (longer than before :D).

I changed the method writeAudioDataToFile() so that now releasing and so on is done there, too (after isRecording is set to false in the stopRecording() method). I do also not create a second Thread anymore, but handle all things in the RecorderThread.

So this seems to work for me now:

private void writeAudioDataToFile() {
    byte data[] = new byte[bufferSize];
    String filename = getTempFilename();
    FileOutputStream os = null;

    try {
        os = new FileOutputStream(filename);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }

    int read = 0;

    if (null != os) {
        while (isRecording) {
            read = recorder.read(data, 0, bufferSize);

            if (AudioRecord.ERROR_INVALID_OPERATION != read) {
                try {
                    os.write(data);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        try {
            os.close();
            os.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

        recorder.stop();
        recorder.release();
        recorder = null;

        String fileName = getFilename();
        copyWaveFile(getTempFilename(), fileName);

        final TelephonyManager tm = (TelephonyManager) getActivity().getBaseContext().getSystemService(Context.TELEPHONY_SERVICE);

        final String tmDevice, tmSerial, androidId;
        tmDevice = "" + tm.getDeviceId();
        tmSerial = "" + tm.getSimSerialNumber();
        androidId = "" + android.provider.Settings.Secure.getString(getActivity().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);

        UUID deviceUuid = new UUID(androidId.hashCode(), ((long) tmDevice.hashCode() << 32) | tmSerial.hashCode());
        String deviceId = deviceUuid.toString();
        App.db.insertEntry(deviceId, "Song title", fileName);

        deleteTempFile();

        if (cbox_overdub.isChecked()) {
            phrases.add(playAudio(fileName, true));
        }
    }
}

public void stopRecording() {
    if (null != recorder) {
        Toast.makeText(getActivity(), "Stop Recording", Toast.LENGTH_SHORT).show();
        ((ImageView) getActivity().findViewById(R.id.btnMic)).setImageDrawable(getActivity().getResources().getDrawable(R.drawable.btn_mic));
        isRecording = false;
        mRecordingBlocked = true;

        mProgressDialog = RecorderDialog.newInstance();
        mProgressDialog.show(getFragmentManager(), "progressDialog");
    }
}

private void publishProgress(int progress) {
    ProgressDialog pd = ((ProgressDialog) mProgressDialog.getDialog());
    if (pd != null) pd.setProgress(progress);
}

Thank you for now!

Chris
  • 295
  • 1
  • 4
  • 11
0

A SIGSEGV should not be possible from Java code. Unless this was your JNI code, this would indicate a flaw in the firmware.

If you are running modded firmware, please pass this information along to the firmware modders.

If you are running the original firmware that came with your device, and this only occurs on one device, you can try to contact the device manufacturer.

If you are seeing this on more than one device with original firmware, and you can create a sample project that reproduces the error, post the project and the stack trace on the Android issue tracker, assuming this issue is not already reported there.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • You're right. I'm using modded firmware (Cyanogenmod 7). If I find out that this is no issue with my source code but with the system I will do as you supposed. – Chris Jun 30 '12 at 14:57
0

The documentation states:

In order to receive the respective callback associated with these listeners, applications are required to create MediaRecorder objects on threads with a Looper running (the main UI thread by default already has a Looper running).

Make sure you create the recorder on the UI thread. Perhaps also call its methods on the UI thread.

grebulon
  • 7,697
  • 5
  • 42
  • 66
0

None of the above solutions worked for me. Instead I increased the buffer in the constructor of the AudioRecord.

The minimum buffersize recommends something between 1k and 4k depending on the parameters and device. It seems my algorithm is too slow to read the data from the buffer before the buffer is full so I got the segv (to be honest I am just guessing the consequences).

After I enhanced the buffer to a value twice as much as I need data from the AudioRecord it works like a charm.

            bufferSize = AudioRecord.getMinBufferSize(sampleAudioBitRate,
                    AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);

            if (bufferSize < sampleSize * 2)
                bufferSize = sampleSize * 2;

            audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleAudioBitRate,
                    AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
            if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
                Log.w(TAG, "audioRecord not initialized");
                return;
            }
mikes
  • 2,323
  • 1
  • 16
  • 11